Skip to content

Commit

Permalink
Add runshell and runshellx
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Aug 21, 2023
1 parent 7b2786e commit 4c34583
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ path = "src/main.rs"
[dependencies]
anyhow = "1.0"
argh = "0.1"
bitflags = "2.3"
console = "0.15"
rustyline = { version = "12.0", default-features = false }

Expand Down
8 changes: 5 additions & 3 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ use console::style;
use fift::core::lexer::LexerPosition;
use fift::core::{Environment, SourceBlock};

use self::args::CmdArgsUtils;
use self::env::SystemEnvironment;
use self::input::LineReader;
use self::modules::*;
use self::util::{ArgsOrVersion, RestArgs, RestArgsDelimiter};

mod args;
mod env;
mod input;
mod util;

mod modules;

/// A simple Fift interpreter. Type `bye` to quie,
/// or `words` to get a list of all commands
#[derive(FromArgs)]
Expand Down Expand Up @@ -101,7 +102,8 @@ fn main() -> Result<ExitCode> {
// Prepare Fift context
let mut ctx = fift::Context::new(&mut env, &mut stdout)
.with_basic_modules()?
.with_module(CmdArgsUtils::new(rest))?;
.with_module(CmdArgsUtils::new(rest))?
.with_module(ShellUtils)?;

for source_block in source_blocks {
ctx.add_source_block(source_block);
Expand Down
File renamed without changes.
5 changes: 5 additions & 0 deletions cli/src/modules/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub use self::args::CmdArgsUtils;
pub use self::shell::ShellUtils;

mod args;
mod shell;
103 changes: 103 additions & 0 deletions cli/src/modules/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::io::{Read, Write};
use std::process::Stdio;

use anyhow::{Context as _, Result};
use bitflags::bitflags;

use fift::core::*;

pub struct ShellUtils;

#[fift_module]
impl ShellUtils {
// runshell (cmd:string args:tuple(string...) -- exit_code:int)
// runshellx (cmd:string args:tuple(string...) [stdin:string] mode:int -- [stdout:string/bytes] [stderr:string] exit_code:int)
#[cmd(name = "runshell", stack, args(mode = Some(ShellMode::DEFAULT)))]
#[cmd(name = "runshellx", stack, args(mode = None))]
fn interpret_run_shell(stack: &mut Stack, mode: Option<ShellMode>) -> Result<()> {
let mode = match mode {
Some(m) => m,
None => ShellMode::from_bits_retain(stack.pop_smallint_range(0, 7)? as u8),
};

let mut stdin = None;
let (stdin_descr, stdin) = if mode.contains(ShellMode::WRITE_STDIN) {
let value = stdin.insert(stack.pop()?);
let value = if mode.contains(ShellMode::STDIN_AS_BYTES) {
value.as_bytes()?
} else {
value.as_string()?.as_bytes()
};

(Stdio::piped(), value)
} else {
(Stdio::null(), [].as_slice())
};

let args = stack.pop_tuple()?;
let args = args
.iter()
.map(|arg| arg.as_string())
.collect::<Result<Vec<_>>>()?;

let cmd = stack.pop_string()?;

let mut child = std::process::Command::new(cmd.as_ref())
.args(args)
.stdin(stdin_descr)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.context("Failed to spawn a child process")?;

if let Some(mut child_stdin) = child.stdin.take() {
child_stdin
.write_all(stdin)
.context("Failed to write to stdin")?;
}

let exit_code = child
.wait()?
.code()
.context("The child process was terminated by signal")?;

if mode.contains(ShellMode::READ_STDOUT) {
let mut bytes = Vec::new();
if let Some(mut stdout) = child.stdout.take() {
stdout.read_to_end(&mut bytes)?;
}
if mode.contains(ShellMode::STDOUT_AS_BYTES) {
stack.push(bytes)?;
} else {
stack.push(String::from_utf8_lossy(&bytes).to_string())?;
}
}

if mode.contains(ShellMode::READ_STDERR) {
let mut bytes = Vec::new();
if let Some(mut stderr) = child.stderr.take() {
stderr.read_to_end(&mut bytes)?;
}
stack.push(String::from_utf8_lossy(&bytes).to_string())?;
}

stack.push_int(exit_code)
}
}

bitflags! {
struct ShellMode: u8 {
/// +1 = use stdin as string from stack (empty otherwise)
const WRITE_STDIN = 1;
/// +2 = push stdout as string on stack after execution
const READ_STDOUT = 2;
/// +4 = push stderr as string on stack after execution
const READ_STDERR = 4;
/// +8 = if stdin is present it is required to be bytes
const STDIN_AS_BYTES = 8;
/// +16 = if stdout is present it is required to be bytes
const STDOUT_AS_BYTES = 16;

const DEFAULT = 0;
}
}

0 comments on commit 4c34583

Please sign in to comment.