Implement shell/1 and shell/2

This commit is contained in:
Adrián Arroyo Calle
2021-10-11 22:35:05 +02:00
parent 44fc61ed86
commit 391cda919f
3 changed files with 60 additions and 1 deletions

View File

@@ -301,6 +301,7 @@ pub(crate) enum SystemClauseType {
GetEnv,
SetEnv,
UnsetEnv,
Shell,
PID,
CharsBase64,
DevourWhitespace,
@@ -592,6 +593,7 @@ impl SystemClauseType {
&SystemClauseType::GetEnv => clause_name!("$getenv"),
&SystemClauseType::SetEnv => clause_name!("$setenv"),
&SystemClauseType::UnsetEnv => clause_name!("$unsetenv"),
&SystemClauseType::Shell => clause_name!("$shell"),
&SystemClauseType::PID => clause_name!("$pid"),
&SystemClauseType::CharsBase64 => clause_name!("$chars_base64"),
&SystemClauseType::LoadLibraryAsStream => clause_name!("$load_library_as_stream"),
@@ -813,6 +815,7 @@ impl SystemClauseType {
("$getenv", 2) => Some(SystemClauseType::GetEnv),
("$setenv", 2) => Some(SystemClauseType::SetEnv),
("$unsetenv", 1) => Some(SystemClauseType::UnsetEnv),
("$shell", 2) => Some(SystemClauseType::Shell),
("$pid", 1) => Some(SystemClauseType::PID),
("$chars_base64", 4) => Some(SystemClauseType::CharsBase64),
("$load_library_as_stream", 3) => Some(SystemClauseType::LoadLibraryAsStream),

View File

@@ -15,6 +15,8 @@
:- module(os, [getenv/2,
setenv/2,
unsetenv/1,
shell/1,
shell/2,
pid/1]).
:- use_module(library(error)).
@@ -34,9 +36,13 @@ unsetenv(Key) :-
must_be_env_var(Key),
'$unsetenv'(Key).
shell(Command) :- shell(Command, 0).
shell(Command, Status) :-
'$shell'(Command, Status).
pid(PID) :-
can_be(integer, PID),
'$pid'(PID).
'$pid'(PID).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
For now, we only support a restricted subset of variable names.

View File

@@ -5382,6 +5382,56 @@ impl MachineState {
let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
env::remove_var(key);
}
&SystemClauseType::Shell => {
// shell executes a command in a system shell
// the code looks for a SHELL env var to do it in a UNIX-style
// if not found, the code looks for COMSPEC env var to do it in a DOS-style
// the output is printed directly to stdout
// the output status code is returned after finishing
fn command_result(machine: &mut MachineState, command: std::io::Result<process::ExitStatus>) {
match command {
Ok(status) => {
match status.code() {
Some(code) => {
let addr = machine.heap.put_constant(Constant::Integer(Rc::new(Integer::from(code))));
(machine.unify_fn)(machine, machine[temp_v!(2)], addr);
}
_ => {
machine.fail = true;
}
}
}
_ => {
machine.fail = true;
}
}
}
let command = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
match env::var("SHELL") {
Ok(value) => {
let command = process::Command::new(&value)
.arg("-c")
.arg(command)
.status();
command_result(self, command);
}
_ => {
match env::var("COMSPEC") {
Ok(value) => {
let command = process::Command::new(&value)
.arg("/C")
.arg(command)
.status();
command_result(self, command);
}
_ => {
self.fail = true;
}
}
}
};
}
&SystemClauseType::PID => {
let a1 = self[temp_v!(1)];
let pid = process::id();