Merge pull request #632 from triska/path_canonical
ADDED: path_canonical/2, obtaining the canonical absolute path.
This commit is contained in:
@@ -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)),
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user