]> Repositorios git - scryer-prolog.git/commitdiff
ADDED: library(os), reasoning about environment variables.
authorMarkus Triska <[email protected]>
Sat, 11 Jul 2020 16:41:18 +0000 (18:41 +0200)
committerMarkus Triska <[email protected]>
Sat, 11 Jul 2020 19:00:25 +0000 (21:00 +0200)
Together with library(files), this addresses #511.

README.md
src/clause_types.rs
src/lib/error.pl
src/lib/os.pl [new file with mode: 0644]
src/machine/system_calls.rs

index 28bed19de09274d25e4e45a520ffff38ed33e6db..6c431ac64b973eeffceae46a3877d8a70500dd96 100644 (file)
--- a/README.md
+++ b/README.md
@@ -442,6 +442,8 @@ The modules that ship with Scryer&nbsp;Prolog are also called
   Predicates for opening and accepting TCP connections as streams.
   TLS negotiation is performed via the option `tls(true)` in
   `socket_client_open/3`, yielding secure encrypted connections.
+* [`os`](src/lib/os.pl)
+  Predicates for reasoning about environment&nbsp;variables.
 * [`crypto`](src/lib/crypto.pl)
   Cryptographically secure random numbers and hashes, HMAC-based
   key derivation (HKDF), password-based key derivation (PBKDF2),
index 66222a6dd7e577bb7b636f9db26b331d745a2409..64e3c2ddf50fa0bc3cc63ad7470cfaf491a6061a 100644 (file)
@@ -306,6 +306,9 @@ pub enum SystemClauseType {
     Ed25519KeyPairPublicKey,
     LoadHTML,
     LoadXML,
+    GetEnv,
+    SetEnv,
+    UnsetEnv,
 }
 
 impl SystemClauseType {
@@ -510,6 +513,9 @@ impl SystemClauseType {
             &SystemClauseType::Ed25519KeyPairPublicKey => clause_name!("$ed25519_keypair_public_key"),
             &SystemClauseType::LoadHTML => clause_name!("$load_html"),
             &SystemClauseType::LoadXML => clause_name!("$load_xml"),
+            &SystemClauseType::GetEnv => clause_name!("$getenv"),
+            &SystemClauseType::SetEnv => clause_name!("$setenv"),
+            &SystemClauseType::UnsetEnv => clause_name!("$unsetenv"),
         }
     }
 
@@ -694,6 +700,9 @@ impl SystemClauseType {
             ("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
             ("$load_html", 3) => Some(SystemClauseType::LoadHTML),
             ("$load_xml", 3) => Some(SystemClauseType::LoadXML),
+            ("$getenv", 2) => Some(SystemClauseType::GetEnv),
+            ("$setenv", 2) => Some(SystemClauseType::SetEnv),
+            ("$unsetenv", 1) => Some(SystemClauseType::UnsetEnv),
             _ => None,
         }
     }
index b75dc3103d8c76c1ddbbdccbb6b67b49e02a5921..7e2189c4a6164f40d62fe47c79ddaff7c7ca0385 100644 (file)
@@ -44,6 +44,7 @@ must_be_(var, Term) :-
         ).
 must_be_(integer, Term) :- check_(integer, integer, Term).
 must_be_(atom, Term)    :- check_(atom, atom, Term).
+must_be_(character, T)  :- check_(character, character, T).
 must_be_(list, Term)    :- check_(ilist, list, Term).
 must_be_(type, Term)    :- check_(type, type, Term).
 must_be_(boolean, Term) :- check_(boolean, boolean, Term).
@@ -56,6 +57,10 @@ check_(Pred, Type, Term) :-
 
 boolean(B) :- ( B == true ; B == false ).
 
+character(C) :-
+        atom(C),
+        atom_length(C, 1).
+
 ilist(V) :- var(V), instantiation_error(must_be/2).
 ilist([]).
 ilist([_|Ls]) :- ilist(Ls).
@@ -63,6 +68,7 @@ ilist([_|Ls]) :- ilist(Ls).
 type(type).
 type(integer).
 type(atom).
+type(character).
 type(list).
 type(var).
 type(boolean).
@@ -90,6 +96,7 @@ can_be(Type, Term) :-
 
 can_(integer, Term) :- integer(Term).
 can_(atom, Term)    :- atom(Term).
+can_(character, T)  :- character(T).
 can_(list, Term)    :- list_or_partial_list(Term).
 can_(boolean, Term) :- boolean(Term).
 
diff --git a/src/lib/os.pl b/src/lib/os.pl
new file mode 100644 (file)
index 0000000..35bfc78
--- /dev/null
@@ -0,0 +1,58 @@
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+   Predicates for reasoning about the operating system (OS) environment.
+   Written July 2020 by Markus Triska ([email protected]).
+
+   Lists of characters are used throughout to represent keys and values.
+
+   Example:
+
+       ?- getenv("LANG", Ls).
+          Ls = "en_US.UTF-8"
+       ;  false.
+
+   Public domain code.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+:- module(os, [getenv/2,
+               setenv/2,
+               unsetenv/1]).
+
+:- use_module(library(error)).
+:- use_module(library(charsio)).
+:- use_module(library(lists)).
+
+getenv(Key, Value) :-
+        must_be_env_var(Key),
+        '$getenv'(Key, Value).
+
+setenv(Key, Value) :-
+        must_be_env_var(Key),
+        must_be_chars(Value),
+        '$setenv'(Key, Value).
+
+unsetenv(Key) :-
+        must_be_env_var(Key),
+        '$unsetenv'(Key).
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+   For now, we only support a restricted subset of variable names.
+
+   The reason is that Rust may panic if a key is empty, contains an
+   ASCII equals sign '=' or the NUL character '\0', or when the value
+   contains the NUL character.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+must_be_env_var(Cs) :-
+        must_be_chars(Cs),
+        Cs = [_|_],
+        (   maplist(permitted, Cs) -> true
+        ;   domain_error(env_var, Cs, os)
+        ).
+
+permitted(C) :- char_type(C, alnum).
+permitted(C) :- char_type(C, ascii_punctuation).
+permitted('_').
+
+must_be_chars(Cs) :-
+        must_be(list, Cs),
+        maplist(must_be(character), Cs).
index 6027f570bc146c355cd01dcf870a75e6117ecd2a..e8db40ce60aa36e3b84ef9036d17c6aeafcb030e 100644 (file)
@@ -32,6 +32,7 @@ use std::net::{TcpListener, TcpStream};
 use std::ops::Sub;
 use std::rc::Rc;
 use std::num::NonZeroU32;
+use std::env;
 
 use std::time::{Duration, SystemTime};
 use crate::cpu_time::ProcessTime;
@@ -5637,6 +5638,28 @@ impl MachineState {
                     }
                 }
             }
+            &SystemClauseType::GetEnv => {
+                let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+                match env::var(key) {
+                    Ok(value) => {
+                        let cstr = self.heap.put_complete_string(&value);
+                        self.unify(self[temp_v!(2)], cstr);
+                    }
+                    _ => {
+                        self.fail = true;
+                        return Ok(());
+                    }
+                }
+            }
+            &SystemClauseType::SetEnv => {
+                let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+                let value = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
+                env::set_var(key, value);
+            }
+            &SystemClauseType::UnsetEnv => {
+                let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+                env::remove_var(key);
+            }
         };
 
         return_from_clause!(self.last_call, self)