mirror of https://github.com/vinc/moros.git
Add shell aliases (#357)
* Add shell aliases
* Fix env output error
* Fix sort
* Read aliases from config file
* Simplify arguments parsing
* Fix test
* Clone params to spawn syscall
* Run clippy
* Revert "Clone params to spawn syscall"
This reverts commit 4c91bea196
.
* Disable binary stripping
* Remove exit alias
* Update doc
This commit is contained in:
parent
cd6cdceb73
commit
82882ec355
2
Makefile
2
Makefile
|
@ -28,7 +28,7 @@ user-rust:
|
|||
-C relocation-model=static
|
||||
basename -s .rs src/bin/*.rs | xargs -I {} \
|
||||
cp target/x86_64-moros/release/{} dsk/bin/{}
|
||||
strip dsk/bin/*
|
||||
#strip dsk/bin/*
|
||||
|
||||
bin = target/x86_64-moros/release/bootimage-moros.bin
|
||||
img = disk.img
|
||||
|
|
|
@ -46,7 +46,7 @@ MOROS Lisp v0.1.0
|
|||
> (+ 1 2)
|
||||
3
|
||||
|
||||
> (exit)
|
||||
> (quit)
|
||||
```
|
||||
|
||||
And it can execute a file. For example a file located in `/tmp/fibonacci.lsp`
|
||||
|
|
23
doc/shell.md
23
doc/shell.md
|
@ -1,15 +1,20 @@
|
|||
# MOROS Shell
|
||||
|
||||
## Config
|
||||
|
||||
The shell will read `/ini/shell.sh` during initialization to setup its
|
||||
configuration.
|
||||
|
||||
## Commands
|
||||
|
||||
The main commands have a long name, a one-letter alias, and may have
|
||||
additional common aliases.
|
||||
|
||||
<!--
|
||||
**Alias** command:
|
||||
|
||||
> alias d delete
|
||||
|
||||
<!--
|
||||
**Append** to file:
|
||||
|
||||
> a a.txt
|
||||
|
@ -130,20 +135,26 @@ Which is more efficient than doing:
|
|||
|
||||
Setting a variable in the shell environment is done with the following command:
|
||||
|
||||
> foo = "world"
|
||||
> set foo 42
|
||||
|
||||
And accessing that variable is done with the `$` operator:
|
||||
> set bar "Alice and Bob"
|
||||
|
||||
And accessing a variable is done with the `$` operator:
|
||||
|
||||
> print $foo
|
||||
world
|
||||
42
|
||||
|
||||
> print "hello $foo"
|
||||
hello world
|
||||
> print "Hello $bar"
|
||||
Hello Alice and Bob
|
||||
|
||||
The process environment is copied to the shell environment when a session is
|
||||
started. By convention a process env var should be in uppercase and a shell
|
||||
env var should be lowercase.
|
||||
|
||||
Unsetting a variable is done like this:
|
||||
|
||||
> unset foo
|
||||
|
||||
## Globbing
|
||||
|
||||
MOROS Shell support filename expansion or globbing for `*` and `?` wildcard
|
||||
|
|
BIN
dsk/bin/clear
BIN
dsk/bin/clear
Binary file not shown.
BIN
dsk/bin/halt
BIN
dsk/bin/halt
Binary file not shown.
BIN
dsk/bin/hello
BIN
dsk/bin/hello
Binary file not shown.
BIN
dsk/bin/print
BIN
dsk/bin/print
Binary file not shown.
BIN
dsk/bin/reboot
BIN
dsk/bin/reboot
Binary file not shown.
BIN
dsk/bin/sleep
BIN
dsk/bin/sleep
Binary file not shown.
|
@ -0,0 +1,28 @@
|
|||
# Command shortcuts
|
||||
alias p print
|
||||
alias c copy
|
||||
alias d delete
|
||||
alias del delete
|
||||
alias e edit
|
||||
alias f find
|
||||
alias g goto
|
||||
alias go goto
|
||||
alias h help
|
||||
alias l list
|
||||
alias m move
|
||||
alias q quit
|
||||
alias r read
|
||||
alias w write
|
||||
alias sh shell
|
||||
alias dsk disk
|
||||
alias mem memory
|
||||
alias kbd keyboard
|
||||
|
||||
# Unix compatibility
|
||||
# alias cd goto
|
||||
# alias cp copy
|
||||
# alias echo print
|
||||
# alias exit quit
|
||||
# alias ls list
|
||||
# alias mv move
|
||||
# alias rm delete
|
|
@ -14,10 +14,9 @@ fn main(boot_info: &'static BootInfo) -> ! {
|
|||
print!("\x1b[?25h"); // Enable cursor
|
||||
loop {
|
||||
if let Some(cmd) = option_env!("MOROS_CMD") {
|
||||
let mut env = usr::shell::default_env();
|
||||
let prompt = usr::shell::prompt_string(true);
|
||||
println!("{}{}", prompt, cmd);
|
||||
usr::shell::exec(cmd, &mut env);
|
||||
usr::shell::exec(cmd);
|
||||
sys::acpi::shutdown();
|
||||
} else {
|
||||
user_boot();
|
||||
|
|
|
@ -118,7 +118,7 @@ fn repl() -> usr::shell::ExitCode {
|
|||
prompt.history.load(history_file);
|
||||
|
||||
while let Some(line) = prompt.input(&prompt_string) {
|
||||
if line == "exit" || line == "quit" {
|
||||
if line == "quit" {
|
||||
break;
|
||||
}
|
||||
if line.is_empty() {
|
||||
|
|
|
@ -86,7 +86,7 @@ impl Chess {
|
|||
while let Some(cmd) = prompt.input(&prompt_string) {
|
||||
let args: Vec<&str> = cmd.trim().split(' ').collect();
|
||||
match args[0] {
|
||||
"q" | "quit" | "exit" => break,
|
||||
"q" | "quit" => break,
|
||||
"h" | "help" => self.cmd_help(args),
|
||||
"i" | "init" => self.cmd_init(args),
|
||||
"t" | "time" => self.cmd_time(args),
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
use crate::{sys, usr};
|
||||
|
||||
pub fn main(args: &[&str]) -> usr::shell::ExitCode {
|
||||
if args.len() == 1 {
|
||||
for (key, val) in sys::process::envs() {
|
||||
println!("{}={}", key, val);
|
||||
match args.len() {
|
||||
1 => {
|
||||
for (key, val) in sys::process::envs() {
|
||||
println!("{:10} \"{}\"", key, val);
|
||||
}
|
||||
usr::shell::ExitCode::CommandSuccessful
|
||||
}
|
||||
} else {
|
||||
for arg in args[1..].iter() {
|
||||
if let Some(i) = arg.find('=') {
|
||||
let (key, mut val) = arg.split_at(i);
|
||||
val = &val[1..];
|
||||
sys::process::set_env(key, val);
|
||||
println!("{}={}", key, val);
|
||||
2 => {
|
||||
let key = args[1];
|
||||
if let Some(val) = sys::process::env(key) {
|
||||
println!("{}", val);
|
||||
usr::shell::ExitCode::CommandSuccessful
|
||||
} else {
|
||||
error!("Error: could not parse '{}'", arg);
|
||||
return usr::shell::ExitCode::CommandError;
|
||||
error!("Could not get '{}'", key);
|
||||
usr::shell::ExitCode::CommandError
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
sys::process::set_env(args[1], args[2]);
|
||||
usr::shell::ExitCode::CommandSuccessful
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid number of arguments");
|
||||
usr::shell::ExitCode::CommandError
|
||||
}
|
||||
}
|
||||
usr::shell::ExitCode::CommandSuccessful
|
||||
}
|
||||
|
|
|
@ -32,10 +32,11 @@ pub fn copy_files(verbose: bool) {
|
|||
create_dev("/dev/random", DeviceType::Random, verbose);
|
||||
create_dev("/dev/console", DeviceType::Console, verbose);
|
||||
|
||||
copy_file("/ini/boot.sh", include_bytes!("../../dsk/ini/boot.sh"), verbose);
|
||||
copy_file("/ini/banner.txt", include_bytes!("../../dsk/ini/banner.txt"), verbose);
|
||||
copy_file("/ini/version.txt", include_bytes!("../../dsk/ini/version.txt"), verbose);
|
||||
copy_file("/ini/boot.sh", include_bytes!("../../dsk/ini/boot.sh"), verbose);
|
||||
copy_file("/ini/palette.csv", include_bytes!("../../dsk/ini/palette.csv"), verbose);
|
||||
copy_file("/ini/shell.sh", include_bytes!("../../dsk/ini/shell.sh"), verbose);
|
||||
copy_file("/ini/version.txt", include_bytes!("../../dsk/ini/version.txt"), verbose);
|
||||
|
||||
create_dir("/ini/lisp", verbose);
|
||||
copy_file("/ini/lisp/core.lsp", include_bytes!("../../dsk/ini/lisp/core.lsp"), verbose);
|
||||
|
|
|
@ -294,8 +294,7 @@ fn default_env() -> Rc<RefCell<Env>> {
|
|||
data.insert("system".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let cmd = string(&args[0])?;
|
||||
let mut env = usr::shell::default_env();
|
||||
let res = usr::shell::exec(&cmd, &mut env);
|
||||
let res = usr::shell::exec(&cmd);
|
||||
Ok(Exp::Num(res as u8 as f64))
|
||||
}));
|
||||
data.insert("print".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
|
@ -673,7 +672,7 @@ fn repl(env: &mut Rc<RefCell<Env>>) -> usr::shell::ExitCode {
|
|||
prompt.completion.set(&lisp_completer);
|
||||
|
||||
while let Some(line) = prompt.input(&prompt_string) {
|
||||
if line == "(exit)" || line == "(quit)" {
|
||||
if line == "(quit)" {
|
||||
break;
|
||||
}
|
||||
if line.is_empty() {
|
||||
|
|
284
src/usr/shell.rs
284
src/usr/shell.rs
|
@ -14,13 +14,13 @@ use alloc::string::{String, ToString};
|
|||
// TODO: Scan /bin
|
||||
const AUTOCOMPLETE_COMMANDS: [&str; 35] = [
|
||||
"2048", "base64", "calc", "colors", "copy", "date", "delete", "dhcp", "disk", "edit",
|
||||
"env", "exit", "geotime", "goto", "help", "hex", "host", "http", "httpd", "install",
|
||||
"keyboard", "lisp", "list", "memory", "move", "net", "pci", "read",
|
||||
"env", "geotime", "goto", "help", "hex", "host", "http", "httpd", "install",
|
||||
"keyboard", "lisp", "list", "memory", "move", "net", "pci", "quit", "read",
|
||||
"shell", "socket", "tcp", "time", "user", "vga", "write"
|
||||
];
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ExitCode {
|
||||
CommandSuccessful = 0,
|
||||
CommandUnknown = 1,
|
||||
|
@ -28,6 +28,23 @@ pub enum ExitCode {
|
|||
ShellExit = 255,
|
||||
}
|
||||
|
||||
struct Config {
|
||||
env: BTreeMap<String, String>,
|
||||
aliases: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn new() -> Config {
|
||||
let aliases = BTreeMap::new();
|
||||
let mut env = BTreeMap::new();
|
||||
for (key, val) in sys::process::envs() {
|
||||
env.insert(key, val); // Copy the process environment to the shell environment
|
||||
}
|
||||
env.insert("DIR".to_string(), sys::process::dir());
|
||||
Config { env, aliases }
|
||||
}
|
||||
}
|
||||
|
||||
fn autocomplete_commands() -> Vec<String> {
|
||||
let mut res = Vec::new();
|
||||
for cmd in AUTOCOMPLETE_COMMANDS {
|
||||
|
@ -38,7 +55,6 @@ fn autocomplete_commands() -> Vec<String> {
|
|||
res.push(file.name());
|
||||
}
|
||||
}
|
||||
res.sort();
|
||||
res
|
||||
}
|
||||
|
||||
|
@ -69,6 +85,7 @@ fn shell_completer(line: &str) -> Vec<String> {
|
|||
}
|
||||
}
|
||||
}
|
||||
entries.sort();
|
||||
entries
|
||||
}
|
||||
|
||||
|
@ -79,19 +96,6 @@ pub fn prompt_string(success: bool) -> String {
|
|||
format!("{}>{} ", if success { csi_color } else { csi_error }, csi_reset)
|
||||
}
|
||||
|
||||
pub fn default_env() -> BTreeMap<String, String> {
|
||||
let mut env = BTreeMap::new();
|
||||
|
||||
// Copy the process environment to the shell environment
|
||||
for (key, val) in sys::process::envs() {
|
||||
env.insert(key, val);
|
||||
}
|
||||
|
||||
env.insert("DIR".to_string(), sys::process::dir());
|
||||
|
||||
env
|
||||
}
|
||||
|
||||
fn is_globbing(arg: &str) -> bool {
|
||||
let arg: Vec<char> = arg.chars().collect();
|
||||
let n = arg.len();
|
||||
|
@ -124,10 +128,10 @@ fn glob_to_regex(pattern: &str) -> String {
|
|||
fn glob(arg: &str) -> Vec<String> {
|
||||
let mut matches = Vec::new();
|
||||
if is_globbing(arg) {
|
||||
let (dir, pattern) = if arg.contains("/") {
|
||||
(fs::dirname(&arg).to_string(), fs::filename(&arg).to_string())
|
||||
let (dir, pattern) = if arg.contains('/') {
|
||||
(fs::dirname(arg).to_string(), fs::filename(arg).to_string())
|
||||
} else {
|
||||
(sys::process::dir().clone(), arg.to_string())
|
||||
(sys::process::dir(), arg.to_string())
|
||||
};
|
||||
|
||||
let re = Regex::new(&glob_to_regex(&pattern));
|
||||
|
@ -192,7 +196,7 @@ pub fn split_args(cmd: &str) -> Vec<String> {
|
|||
args
|
||||
}
|
||||
|
||||
fn proc(args: &[&str]) -> ExitCode {
|
||||
fn cmd_proc(args: &[&str]) -> ExitCode {
|
||||
match args.len() {
|
||||
1 => {
|
||||
ExitCode::CommandSuccessful
|
||||
|
@ -222,7 +226,7 @@ fn proc(args: &[&str]) -> ExitCode {
|
|||
}
|
||||
}
|
||||
|
||||
fn change_dir(args: &[&str], env: &mut BTreeMap<String, String>) -> ExitCode {
|
||||
fn cmd_change_dir(args: &[&str], config: &mut Config) -> ExitCode {
|
||||
match args.len() {
|
||||
1 => {
|
||||
println!("{}", sys::process::dir());
|
||||
|
@ -235,7 +239,7 @@ fn change_dir(args: &[&str], env: &mut BTreeMap<String, String>) -> ExitCode {
|
|||
}
|
||||
if api::fs::is_dir(&pathname) {
|
||||
sys::process::set_dir(&pathname);
|
||||
env.insert("DIR".to_string(), sys::process::dir());
|
||||
config.env.insert("DIR".to_string(), sys::process::dir());
|
||||
ExitCode::CommandSuccessful
|
||||
} else {
|
||||
error!("File not found '{}'", pathname);
|
||||
|
@ -248,27 +252,86 @@ fn change_dir(args: &[&str], env: &mut BTreeMap<String, String>) -> ExitCode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn exec(cmd: &str, env: &mut BTreeMap<String, String>) -> ExitCode {
|
||||
fn cmd_alias(args: &[&str], config: &mut Config) -> ExitCode {
|
||||
if args.len() != 3 {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} alias {}<key> <val>{1}", csi_title, csi_reset, csi_option);
|
||||
return usr::shell::ExitCode::CommandError;
|
||||
}
|
||||
config.aliases.insert(args[1].to_string(), args[2].to_string());
|
||||
ExitCode::CommandSuccessful
|
||||
}
|
||||
|
||||
fn cmd_unalias(args: &[&str], config: &mut Config) -> ExitCode {
|
||||
if args.len() != 2 {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} unalias {}<key>{1}", csi_title, csi_reset, csi_option);
|
||||
return usr::shell::ExitCode::CommandError;
|
||||
}
|
||||
|
||||
if config.aliases.remove(&args[1].to_string()).is_none() {
|
||||
error!("Error: could not unalias '{}'", args[1]);
|
||||
return usr::shell::ExitCode::CommandError;
|
||||
}
|
||||
|
||||
ExitCode::CommandSuccessful
|
||||
}
|
||||
|
||||
fn cmd_set(args: &[&str], config: &mut Config) -> ExitCode {
|
||||
if args.len() != 3 {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} set {}<key> <val>{1}", csi_title, csi_reset, csi_option);
|
||||
return usr::shell::ExitCode::CommandError;
|
||||
}
|
||||
|
||||
config.env.insert(args[1].to_string(), args[2].to_string());
|
||||
ExitCode::CommandSuccessful
|
||||
}
|
||||
|
||||
fn cmd_unset(args: &[&str], config: &mut Config) -> ExitCode {
|
||||
if args.len() != 2 {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} unset {}<key>{1}", csi_title, csi_reset, csi_option);
|
||||
return usr::shell::ExitCode::CommandError;
|
||||
}
|
||||
|
||||
if config.env.remove(&args[1].to_string()).is_none() {
|
||||
error!("Error: could not unset '{}'", args[1]);
|
||||
return usr::shell::ExitCode::CommandError;
|
||||
}
|
||||
|
||||
ExitCode::CommandSuccessful
|
||||
}
|
||||
|
||||
fn exec_with_config(cmd: &str, config: &mut Config) -> ExitCode {
|
||||
let mut cmd = cmd.to_string();
|
||||
|
||||
// Replace `$key` with its value in the environment or an empty string
|
||||
let re = Regex::new("\\$\\w+");
|
||||
while let Some((a, b)) = re.find(&cmd) {
|
||||
let key: String = cmd.chars().skip(a + 1).take(b - a - 1).collect();
|
||||
let val = env.get(&key).map_or("", String::as_str);
|
||||
cmd = cmd.replace(&format!("${}", key), &val);
|
||||
let val = config.env.get(&key).map_or("", String::as_str);
|
||||
cmd = cmd.replace(&format!("${}", key), val);
|
||||
}
|
||||
|
||||
// Set env var like `foo=42` or `bar = "Hello, World!"
|
||||
if Regex::new("^\\w+ *= *\\S*$").is_match(&cmd) {
|
||||
let mut iter = cmd.splitn(2, '=');
|
||||
let key = iter.next().unwrap_or("").trim().to_string();
|
||||
let val = iter.next().unwrap_or("").trim().to_string();
|
||||
env.insert(key, val);
|
||||
return ExitCode::CommandSuccessful
|
||||
let mut args = split_args(&cmd);
|
||||
|
||||
// Replace command alias
|
||||
if let Some(alias) = config.aliases.get(&args[0]) {
|
||||
args.remove(0);
|
||||
for arg in alias.split(' ').rev() {
|
||||
args.insert(0, arg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
let args = split_args(&cmd);
|
||||
let mut args: Vec<&str> = args.iter().map(String::as_str).collect();
|
||||
|
||||
// Redirections like `print hello => /tmp/hello`
|
||||
|
@ -326,62 +389,52 @@ pub fn exec(cmd: &str, env: &mut BTreeMap<String, String>) -> ExitCode {
|
|||
}
|
||||
|
||||
let res = match args[0] {
|
||||
"" => ExitCode::CommandSuccessful,
|
||||
"a" => ExitCode::CommandUnknown,
|
||||
"b" => ExitCode::CommandUnknown,
|
||||
"c" | "copy" => usr::copy::main(&args),
|
||||
"d" | "del" | "delete" => usr::delete::main(&args),
|
||||
"e" | "edit" => usr::editor::main(&args),
|
||||
"f" | "find" => usr::find::main(&args),
|
||||
"g" | "go" | "goto" => change_dir(&args, env),
|
||||
"h" | "help" => usr::help::main(&args),
|
||||
"i" => ExitCode::CommandUnknown,
|
||||
"j" => ExitCode::CommandUnknown,
|
||||
"k" => ExitCode::CommandUnknown,
|
||||
"l" | "list" => usr::list::main(&args),
|
||||
"m" | "move" => usr::r#move::main(&args),
|
||||
"n" => ExitCode::CommandUnknown,
|
||||
"o" => ExitCode::CommandUnknown,
|
||||
"q" | "quit" | "exit" => ExitCode::ShellExit,
|
||||
"r" | "read" => usr::read::main(&args),
|
||||
"s" => ExitCode::CommandUnknown,
|
||||
"t" => ExitCode::CommandUnknown,
|
||||
"u" => ExitCode::CommandUnknown,
|
||||
"v" => ExitCode::CommandUnknown,
|
||||
"w" | "write" => usr::write::main(&args),
|
||||
"x" => ExitCode::CommandUnknown,
|
||||
"y" => ExitCode::CommandUnknown,
|
||||
"z" => ExitCode::CommandUnknown,
|
||||
"vga" => usr::vga::main(&args),
|
||||
"sh" | "shell" => usr::shell::main(&args),
|
||||
"calc" => usr::calc::main(&args),
|
||||
"base64" => usr::base64::main(&args),
|
||||
"date" => usr::date::main(&args),
|
||||
"env" => usr::env::main(&args),
|
||||
"hex" => usr::hex::main(&args),
|
||||
"net" => usr::net::main(&args),
|
||||
"dhcp" => usr::dhcp::main(&args),
|
||||
"http" => usr::http::main(&args),
|
||||
"httpd" => usr::httpd::main(&args),
|
||||
"socket" => usr::socket::main(&args),
|
||||
"tcp" => usr::tcp::main(&args),
|
||||
"host" => usr::host::main(&args),
|
||||
"install" => usr::install::main(&args),
|
||||
"geotime" => usr::geotime::main(&args),
|
||||
"colors" => usr::colors::main(&args),
|
||||
"dsk" | "disk" => usr::disk::main(&args),
|
||||
"user" => usr::user::main(&args),
|
||||
"mem" | "memory" => usr::memory::main(&args),
|
||||
"kb" | "keyboard" => usr::keyboard::main(&args),
|
||||
"lisp" => usr::lisp::main(&args),
|
||||
"chess" => usr::chess::main(&args),
|
||||
"beep" => usr::beep::main(&args),
|
||||
"elf" => usr::elf::main(&args),
|
||||
"pci" => usr::pci::main(&args),
|
||||
"2048" => usr::pow::main(&args),
|
||||
"time" => usr::time::main(&args),
|
||||
"proc" => proc(&args),
|
||||
_ => {
|
||||
"" => ExitCode::CommandSuccessful,
|
||||
"2048" => usr::pow::main(&args),
|
||||
"alias" => cmd_alias(&args, config),
|
||||
"base64" => usr::base64::main(&args),
|
||||
"beep" => usr::beep::main(&args),
|
||||
"calc" => usr::calc::main(&args),
|
||||
"chess" => usr::chess::main(&args),
|
||||
"colors" => usr::colors::main(&args),
|
||||
"copy" => usr::copy::main(&args),
|
||||
"date" => usr::date::main(&args),
|
||||
"delete" => usr::delete::main(&args),
|
||||
"dhcp" => usr::dhcp::main(&args),
|
||||
"disk" => usr::disk::main(&args),
|
||||
"edit" => usr::editor::main(&args),
|
||||
"elf" => usr::elf::main(&args),
|
||||
"env" => usr::env::main(&args),
|
||||
"find" => usr::find::main(&args),
|
||||
"geotime" => usr::geotime::main(&args),
|
||||
"goto" => cmd_change_dir(&args, config),
|
||||
"help" => usr::help::main(&args),
|
||||
"hex" => usr::hex::main(&args),
|
||||
"host" => usr::host::main(&args),
|
||||
"http" => usr::http::main(&args),
|
||||
"httpd" => usr::httpd::main(&args),
|
||||
"install" => usr::install::main(&args),
|
||||
"keyboard" => usr::keyboard::main(&args),
|
||||
"lisp" => usr::lisp::main(&args),
|
||||
"list" => usr::list::main(&args),
|
||||
"memory" => usr::memory::main(&args),
|
||||
"move" => usr::r#move::main(&args),
|
||||
"net" => usr::net::main(&args),
|
||||
"pci" => usr::pci::main(&args),
|
||||
"proc" => cmd_proc(&args),
|
||||
"quit" => ExitCode::ShellExit,
|
||||
"read" => usr::read::main(&args),
|
||||
"set" => cmd_set(&args, config),
|
||||
"shell" => usr::shell::main(&args),
|
||||
"socket" => usr::socket::main(&args),
|
||||
"tcp" => usr::tcp::main(&args),
|
||||
"time" => usr::time::main(&args),
|
||||
"unalias" => cmd_unalias(&args, config),
|
||||
"unset" => cmd_unset(&args, config),
|
||||
"user" => usr::user::main(&args),
|
||||
"vga" => usr::vga::main(&args),
|
||||
"write" => usr::write::main(&args),
|
||||
_ => {
|
||||
let mut path = fs::realpath(args[0]);
|
||||
if path.len() > 1 {
|
||||
path = path.trim_end_matches('/').into();
|
||||
|
@ -389,7 +442,7 @@ pub fn exec(cmd: &str, env: &mut BTreeMap<String, String>) -> ExitCode {
|
|||
match syscall::info(&path).map(|info| info.kind()) {
|
||||
Some(FileType::Dir) => {
|
||||
sys::process::set_dir(&path);
|
||||
env.insert("DIR".to_string(), sys::process::dir());
|
||||
config.env.insert("DIR".to_string(), sys::process::dir());
|
||||
ExitCode::CommandSuccessful
|
||||
}
|
||||
Some(FileType::File) => {
|
||||
|
@ -402,12 +455,7 @@ pub fn exec(cmd: &str, env: &mut BTreeMap<String, String>) -> ExitCode {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO: add aliases command instead of hardcoding them
|
||||
let name = match args[0] {
|
||||
"p" => "print",
|
||||
arg => arg,
|
||||
};
|
||||
if api::process::spawn(&format!("/bin/{}", name), &args).is_ok() {
|
||||
if api::process::spawn(&format!("/bin/{}", args[0]), &args).is_ok() {
|
||||
ExitCode::CommandSuccessful
|
||||
} else {
|
||||
error!("Could not execute '{}'", cmd);
|
||||
|
@ -428,7 +476,7 @@ pub fn exec(cmd: &str, env: &mut BTreeMap<String, String>) -> ExitCode {
|
|||
res
|
||||
}
|
||||
|
||||
fn repl(env: &mut BTreeMap<String, String>) -> usr::shell::ExitCode {
|
||||
fn repl(config: &mut Config) -> usr::shell::ExitCode {
|
||||
println!();
|
||||
|
||||
let mut prompt = Prompt::new();
|
||||
|
@ -438,7 +486,7 @@ fn repl(env: &mut BTreeMap<String, String>) -> usr::shell::ExitCode {
|
|||
|
||||
let mut success = true;
|
||||
while let Some(cmd) = prompt.input(&prompt_string(success)) {
|
||||
match exec(&cmd, env) {
|
||||
match exec_with_config(&cmd, config) {
|
||||
ExitCode::CommandSuccessful => {
|
||||
success = true;
|
||||
},
|
||||
|
@ -458,26 +506,37 @@ fn repl(env: &mut BTreeMap<String, String>) -> usr::shell::ExitCode {
|
|||
ExitCode::CommandSuccessful
|
||||
}
|
||||
|
||||
pub fn exec(cmd: &str) -> ExitCode {
|
||||
let mut config = Config::new();
|
||||
exec_with_config(cmd, &mut config)
|
||||
}
|
||||
|
||||
pub fn main(args: &[&str]) -> ExitCode {
|
||||
let mut env = default_env();
|
||||
let mut config = Config::new();
|
||||
|
||||
if let Ok(rc) = fs::read_to_string("/ini/shell.sh") {
|
||||
for cmd in rc.split('\n') {
|
||||
exec_with_config(cmd, &mut config);
|
||||
}
|
||||
}
|
||||
|
||||
if args.len() < 2 {
|
||||
env.insert(0.to_string(), args[0].to_string());
|
||||
config.env.insert(0.to_string(), args[0].to_string());
|
||||
|
||||
repl(&mut env)
|
||||
repl(&mut config)
|
||||
} else {
|
||||
env.insert(0.to_string(), args[1].to_string());
|
||||
config.env.insert(0.to_string(), args[1].to_string());
|
||||
|
||||
// Add script arguments to the environment as `$1`, `$2`, `$3`, ...
|
||||
for (i, arg) in args[2..].iter().enumerate() {
|
||||
env.insert((i + 1).to_string(), arg.to_string());
|
||||
config.env.insert((i + 1).to_string(), arg.to_string());
|
||||
}
|
||||
|
||||
let pathname = args[1];
|
||||
if let Ok(contents) = api::fs::read_to_string(pathname) {
|
||||
for line in contents.split('\n') {
|
||||
if !line.is_empty() {
|
||||
exec(line, &mut env);
|
||||
exec_with_config(line, &mut config);
|
||||
}
|
||||
}
|
||||
ExitCode::CommandSuccessful
|
||||
|
@ -496,26 +555,25 @@ fn test_shell() {
|
|||
sys::fs::format_mem();
|
||||
usr::install::copy_files(false);
|
||||
|
||||
let mut env = default_env();
|
||||
|
||||
// Redirect standard output
|
||||
exec("print test1 => /test", &mut env);
|
||||
exec("print test1 => /test");
|
||||
assert_eq!(api::fs::read_to_string("/test"), Ok("test1\n".to_string()));
|
||||
|
||||
// Overwrite content of existing file
|
||||
exec("print test2 => /test", &mut env);
|
||||
exec("print test2 => /test");
|
||||
assert_eq!(api::fs::read_to_string("/test"), Ok("test2\n".to_string()));
|
||||
|
||||
// Redirect standard output explicitely
|
||||
exec("print test3 1=> /test", &mut env);
|
||||
exec("print test3 1=> /test");
|
||||
assert_eq!(api::fs::read_to_string("/test"), Ok("test3\n".to_string()));
|
||||
|
||||
// Redirect standard error explicitely
|
||||
exec("hex /nope 2=> /test", &mut env);
|
||||
exec("hex /nope 2=> /test");
|
||||
assert!(api::fs::read_to_string("/test").unwrap().contains("File not found '/nope'"));
|
||||
|
||||
exec("b = 42", &mut env);
|
||||
exec("print a $b $c d => /test", &mut env);
|
||||
let mut config = Config::new();
|
||||
exec_with_config("set b 42", &mut config);
|
||||
exec_with_config("print a $b $c d => /test", &mut config);
|
||||
assert_eq!(api::fs::read_to_string("/test"), Ok("a 42 d\n".to_string()));
|
||||
|
||||
sys::fs::dismount();
|
||||
|
|
|
@ -6,9 +6,8 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode {
|
|||
let csi_color = Style::color("LightBlue");
|
||||
let csi_reset = Style::reset();
|
||||
let cmd = args[1..].join(" ");
|
||||
let mut env = usr::shell::default_env();
|
||||
let start = clock::realtime();
|
||||
usr::shell::exec(&cmd, &mut env);
|
||||
usr::shell::exec(&cmd);
|
||||
let duration = clock::realtime() - start;
|
||||
println!("{}Executed '{}' in {:.6}s{}", csi_color, cmd, duration, csi_reset);
|
||||
usr::shell::ExitCode::CommandSuccessful
|
||||
|
|
Loading…
Reference in New Issue