`current_time/1` to obtain the current system time, the nonterminal
`format_time//2` to describe strings with dates and times, and
`sleep/1` to slow down a computation.
+* [`files`](src/lib/files.pl)
+ Predicates for reasoning about files and directories, such as
+ `directory_files/2`, `file_exists/1` and `file_size/2`.
* [`cont`](src/lib/cont.pl)
Provides *delimited continuations* via `reset/3` and `shift/1`.
* [`random`](src/lib/random.pl)
CurrentHostname,
CurrentInput,
CurrentOutput,
+ DirectoryFiles,
+ FileSize,
+ FileExists,
+ DirectoryExists,
+ MakeDirectory,
DeleteAttribute,
DeleteHeadAttribute,
DynamicModuleResolution(usize),
&SystemClauseType::CurrentInput => clause_name!("$current_input"),
&SystemClauseType::CurrentHostname => clause_name!("$current_hostname"),
&SystemClauseType::CurrentOutput => clause_name!("$current_output"),
+ &SystemClauseType::DirectoryFiles => clause_name!("$directory_files"),
+ &SystemClauseType::FileSize => clause_name!("$file_size"),
+ &SystemClauseType::FileExists => clause_name!("$file_exists"),
+ &SystemClauseType::DirectoryExists => clause_name!("$directory_exists"),
+ &SystemClauseType::MakeDirectory => clause_name!("$make_directory"),
&SystemClauseType::REPL(REPLCodePtr::CompileBatch) => clause_name!("$compile_batch"),
&SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"),
&SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => {
("$unwind_environments", 0) => Some(SystemClauseType::UnwindEnvironments),
("$unwind_stack", 0) => Some(SystemClauseType::UnwindStack),
("$unify_with_occurs_check", 2) => Some(SystemClauseType::UnifyWithOccursCheck),
+ ("$directory_files", 2) => Some(SystemClauseType::DirectoryFiles),
+ ("$file_size", 2) => Some(SystemClauseType::FileSize),
+ ("$file_exists", 1) => Some(SystemClauseType::FileExists),
+ ("$directory_exists", 1) => Some(SystemClauseType::DirectoryExists),
+ ("$make_directory", 1) => Some(SystemClauseType::MakeDirectory),
("$use_module", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)),
("$use_module_from_file", 1) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile)),
--- /dev/null
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Part of Scryer Prolog.
+
+ Predicates for reasoning about files and directories.
+
+ In this library, directories and files are represented as
+ *lists of characters*. This is an ideal representation:
+
+ -) Lists of characters can be conveniently reasoned about with DCGs
+ and built-in Prolog predicates from library(lists). This alone
+ is already a very compelling argument to use them.
+ -) Other Scryer libraries such as library(http/http_open) also already
+ use lists of characters to represent paths.
+ -) File names are mostly ephemeral, so it is good for efficiency
+ that they can quickly allocated transiently on the heap, leaving the
+ atom table mostly unaffected. Indexing is almost never needed
+ for file names.
+ -) The previous point is also good for security, since the system
+ leaves little trace of which files were even accessed.
+ -) Scryer Prolog represents lists of characters extremely compactly.
+
+ Some Prolog programmers will likely find this representation quite
+ unusual, because files are represented as *atoms* in many systems.
+
+ However, the ISO standard only demands that sources and sinks be
+ *ground* terms, so lists of characters are completely admissible:
+
+ A source/sink is specified as an implementation defined
+ ground term in a call of open/4 (8.11.5). All subsequent
+ references to the source/sink are made by referring to a
+ stream-term (7.10.2) or alias (7.10.2.2).
+
+ I believe that with the advent of Scryer Prolog and its efficient
+ representation of strings as lists of characters, we should take
+ this opportunity for improvement, and in fact extend the use of
+ lists of characters also to other predicates like open/3.
+
+ Please note that we *cannot* simply accept *both* representations,
+ because that would invalidate the type errors raised by this library,
+ and make future extensions for type checking impossible.
+
+ So I ask you: Please try out this representation for a few months.
+ It simplifies working with files considerably. Once we have collected
+ more experience with this representations, please let us consider
+ how to proceed in such a way that we can call it an improvement.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+:- module(files, [directory_files/2,
+ file_size/2,
+ file_exists/1,
+ directory_exists/1,
+ make_directory/1]).
+
+:- use_module(library(error)).
+:- use_module(library(lists)).
+
+list_of_chars(Cs) :-
+ must_be(list, Cs),
+ ( ground(Cs) ->
+ ( member(C, Cs), \+ atom_length(C, 1) ->
+ type_error(char, C, files_and_directories)
+ ; true
+ )
+ ; instantiation_error(s)
+ ).
+
+directory_files(Directory, Files) :-
+ list_of_chars(Directory),
+ can_be(list, Files),
+ '$directory_files'(Directory, Files).
+
+file_size(File, Size) :-
+ list_of_chars(File),
+ can_be(integer, Size),
+ '$file_size'(File, Size).
+
+file_exists(File) :-
+ list_of_chars(File),
+ '$file_exists'(File).
+
+directory_exists(Directory) :-
+ list_of_chars(Directory),
+ '$directory_exists'(Directory).
+
+make_directory(Directory) :-
+ list_of_chars(Directory),
+ '$make_directory'(Directory).
use crate::ref_thread_local::RefThreadLocal;
use std::cmp;
+use std::fs;
use std::collections::BTreeSet;
use std::convert::TryFrom;
use std::io::{ErrorKind, Read, Write};
use std::iter::{once, FromIterator};
-use std::fs::{OpenOptions};
use std::net::{TcpListener, TcpStream};
use std::ops::Sub;
use std::rc::Rc;
}
}
}
+ &SystemClauseType::DirectoryFiles => {
+ let dir = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+ let path = std::path::Path::new(&dir);
+ let mut files = Vec::new();
+
+ if let Ok(entries) = fs::read_dir(path) {
+ for entry in entries {
+ if let Ok(entry) = entry {
+ let name = entry.file_name().into_string().unwrap();
+ files.push(self.heap.put_complete_string(&name));
+ }
+ }
+ }
+
+ let files_list = Addr::HeapCell(self.heap.to_list(files.into_iter()));
+ self.unify(self[temp_v!(2)], files_list);
+ }
+ &SystemClauseType::FileSize => {
+ let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+ let len = Integer::from(fs::metadata(&file).unwrap().len());
+
+ let len = self.heap.to_unifiable(HeapCellValue::Integer(Rc::new(len)));
+
+ self.unify(self[temp_v!(2)], len);
+ }
+ &SystemClauseType::FileExists => {
+ let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+ if !std::path::Path::new(&file).exists() || !fs::metadata(&file).unwrap().is_file() {
+ self.fail = true;
+ return Ok(());
+ }
+ }
+ &SystemClauseType::DirectoryExists => {
+ let directory = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+ if !std::path::Path::new(&directory).exists() || !fs::metadata(&directory).unwrap().is_dir() {
+ self.fail = true;
+ return Ok(());
+ }
+ }
+ &SystemClauseType::MakeDirectory => {
+ let directory = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+
+ match fs::create_dir(directory) {
+ Ok(_) => { }
+ _ => { self.fail = true;
+ return Ok(());
+ }
+ }
+ }
&SystemClauseType::AtEndOfExpansion => {
if self.cp == LocalCodePtr::TopLevel(0, 0) {
self.at_end_of_expansion = true;
let mode =
atom_from!(self, indices, self.store(self.deref(self[temp_v!(2)])));
- let mut open_options = OpenOptions::new();
+ let mut open_options = fs::OpenOptions::new();
let (is_input_file, in_append_mode) =
match mode.as_str() {