Skip to content

Commit

Permalink
refine builtins redirections/pipelines (#32)
Browse files Browse the repository at this point in the history
* refine redirections on aliases

* misc

* wip - add set; minfd; refactoring

* misc

* when builtin output got captured

* fix builtin in mid of pipeline

* misc

* more builtins updates 1

* more builtins updates 2

* more builtins updates 3

* finished updates on builtins

* misc

* misc
  • Loading branch information
mitnk authored May 23, 2021
1 parent fecb1ae commit c324f16
Show file tree
Hide file tree
Showing 30 changed files with 1,019 additions and 390 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Logs

## 0.9.19 - 2021-05-23

- Refine redirections of aliases.
- Fix & imporve redirections/pipelines for builtins.
- Added new beta builtins: set, minfd.

## 0.9.18 - 2021-05-04

- fix compiling issue on 32bit systems.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
edition = "2018"
build = "src/build.rs"
name = "cicada"
version = "0.9.18"
version = "0.9.19"
authors = ["Hugo Wang <[email protected]>"]

description = "A simple Bash-like Unix shell."
Expand Down
16 changes: 14 additions & 2 deletions docs/builtins.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
- [fg](#user-content-fg)
- [history](#user-content-history)
- [jobs](#user-content-jobs)
- [minfd](#user-content-minfd)
- [read](#user-content-read)
- [set](#user-content-set)
- [source](#user-content-source)
- [ulimit](#user-content-ulimit)
- [unalias](#user-content-unalias)
Expand Down Expand Up @@ -137,14 +139,19 @@ $ history add '<the command input>'
Listing all jobs in [job control](https://github.com/mitnk/cicada/blob/master/docs/jobc.md).
See also `bg`, `fg`.

## minfd

Get minimal file descriptor number in current shell process. This builtin
is hardly useful for users, mainly using in debugging cicada.

## read

Read a line from the standard input and split it into fields.

```
read [name ...]
```

Read a line from the standard input and split it into fields.

Reads a single line from the standard input. The line is split into fields as
with word splitting, and the first word is assigned to the first NAME, the
second word to the second NAME, and so on, with any leftover words assigned to
Expand All @@ -168,6 +175,11 @@ $ IFS=:@ read a b c
$ echo $c $b $a
```

## set

(in BETA) Set shell options. Currently ony support `set -e`, same effects
as Bash.

## source

Read and execute commands from the `filename` argument in the current shell
Expand Down
79 changes: 37 additions & 42 deletions src/builtins/alias.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,33 @@
use std::io::Write;

use regex::Regex;

use crate::shell;
use crate::tools;
use crate::types::Tokens;
use crate::types::{Command, CommandLine, CommandResult};
use crate::builtins::utils::print_stderr_with_capture;
use crate::builtins::utils::print_stdout_with_capture;

pub fn run(sh: &mut shell::Shell, cl: &CommandLine, cmd: &Command,
capture: bool) -> CommandResult {
let mut cr = CommandResult::new();
let tokens = cmd.tokens.clone();

pub fn run(sh: &mut shell::Shell, tokens: &Tokens) -> i32 {
if tokens.len() == 1 {
return show_alias_list(sh);
return show_alias_list(sh, cmd, cl, capture);
}

if tokens.len() > 2 {
println_stderr!("alias syntax error");
println_stderr!("alias usage example: alias foo='echo foo'");
return 1;
let info = "alias syntax error: usage: alias foo='echo foo'";
print_stderr_with_capture(info, &mut cr, cl, cmd, capture);
return cr;
}

let input = &tokens[1].1;
let re_single_read;
match Regex::new(r"^[a-zA-Z0-9_\.-]+$") {
Ok(x) => re_single_read = x,
Err(e) => {
println!("cicada: Regex error: {:?}", e);
return 1;
}
}
let re_single_read = Regex::new(r"^[a-zA-Z0-9_\.-]+$").unwrap();
if re_single_read.is_match(input) {
return show_single_alias(sh, input);
}

let re_to_add;
match Regex::new(r"^([a-zA-Z0-9_\.-]+)=(.*)$") {
Ok(x) => re_to_add = x,
Err(e) => {
println!("cicada: Regex error: {:?}", e);
return 1;
}
return show_single_alias(sh, input, cmd, cl, capture);
}

let re_to_add = Regex::new(r"^([a-zA-Z0-9_\.-]+)=(.*)$").unwrap();
for cap in re_to_add.captures_iter(input) {
let name = tools::unquote(&cap[1]);
// due to limitation of `parses::parser_line`,
Expand All @@ -50,27 +40,32 @@ pub fn run(sh: &mut shell::Shell, tokens: &Tokens) -> i32 {
};
sh.add_alias(name.as_str(), value.as_str());
}
0

CommandResult::new()
}

fn show_alias_list(sh: &shell::Shell) -> i32 {
fn show_alias_list(sh: &shell::Shell, cmd: &Command,
cl: &CommandLine, capture: bool) -> CommandResult {
let mut lines = Vec::new();
for (name, value) in sh.get_alias_list() {
println!("alias {}='{}'", name, value);
let line = format!("alias {}='{}'", name, value);
lines.push(line);
}
0
let buffer = lines.join("\n");
let mut cr = CommandResult::new();
print_stdout_with_capture(&buffer, &mut cr, cl, cmd, capture);
cr
}

fn show_single_alias(sh: &shell::Shell, name_to_find: &str) -> i32 {
let mut found = false;
for (name, value) in sh.get_alias_list() {
if name_to_find == name {
println!("alias {}='{}'", name, value);
found = true;
}
}
if !found {
println_stderr!("cicada: alias: {}: not found", name_to_find);
return 1;
fn show_single_alias(sh: &shell::Shell, name_to_find: &str, cmd: &Command,
cl: &CommandLine, capture: bool) -> CommandResult {
let mut cr = CommandResult::new();
if let Some(content) = sh.get_alias_content(name_to_find) {
let info = format!("alias {}='{}'", name_to_find, content);
print_stdout_with_capture(&info, &mut cr, cl, cmd, capture);
} else {
let info = format!("cicada: alias: {}: not found", name_to_find);
print_stderr_with_capture(&info, &mut cr, cl, cmd, capture);
}
0
cr
}
43 changes: 26 additions & 17 deletions src/builtins/bg.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use std::io::Write;

use crate::builtins::utils::print_stderr_with_capture;
use crate::jobc;
use crate::libc;
use crate::shell;
use crate::types;
use crate::shell::Shell;
use crate::types::{CommandResult, CommandLine, Command};

pub fn run(sh: &mut Shell, cl: &CommandLine, cmd: &Command,
capture: bool) -> CommandResult {
let tokens = cmd.tokens.clone();
let mut cr = CommandResult::new();

pub fn run(sh: &mut shell::Shell, tokens: &types::Tokens) -> i32 {
if sh.jobs.is_empty() {
println_stderr!("cicada: bg: no job found");
return 0;
let info = "cicada: bg: no job found";
print_stderr_with_capture(info, &mut cr, cl, cmd, capture);
return cr;
}

let mut job_id = -1;
Expand All @@ -28,13 +32,16 @@ pub fn run(sh: &mut shell::Shell, tokens: &types::Tokens) -> i32 {
match job_str.parse::<i32>() {
Ok(n) => job_id = n,
Err(_) => {
println_stderr!("cicada: bg: invalid job id");
return 1;
let info = "cicada: bg: invalid job id";
print_stderr_with_capture(info, &mut cr, cl, cmd, capture);
return cr;
}
}
}
if job_id == -1 {
println_stderr!("cicada: not job id found");
let info = "cicada: bg: not such job";
print_stderr_with_capture(info, &mut cr, cl, cmd, capture);
return cr;
}

let gid: i32;
Expand All @@ -48,29 +55,31 @@ pub fn run(sh: &mut shell::Shell, tokens: &types::Tokens) -> i32 {

match result {
Some(job) => {
let cmd = if job.cmd.ends_with(" &") {
let info_cmd = if job.cmd.ends_with(" &") {
job.cmd.clone()
} else {
format!("{} &", job.cmd)
};
println_stderr!("{}", &cmd);
print_stderr_with_capture(&info_cmd, &mut cr, cl, cmd, capture);

unsafe {
libc::killpg(job.gid, libc::SIGCONT);
gid = job.gid;
if job.status == "Running" {
println_stderr!("cicada: bg: job {} already in background", job.id);
return 0;
let info = format!("cicada: bg: job {} already in background", job.id);
print_stderr_with_capture(&info, &mut cr, cl, cmd, capture);
return cr;
}
}
}
None => {
println_stderr!("cicada: bg: no such job");
return 1;
let info = "cicada: bg: not such job";
print_stderr_with_capture(&info, &mut cr, cl, cmd, capture);
return cr;
}
}
}

jobc::mark_job_as_running(sh, gid, true);
return 0;
return cr;
}
48 changes: 27 additions & 21 deletions src/builtins/cd.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
use std::env;
use std::io::Write;
use std::path::Path;

use crate::builtins::utils::print_stderr_with_capture;
use crate::parsers;
use crate::shell;
use crate::tools;
use crate::types::{Command, CommandLine, CommandResult};

use crate::types::Tokens;

pub fn run(sh: &mut shell::Shell, tokens: &Tokens) -> i32 {
pub fn run(sh: &mut shell::Shell, cl: &CommandLine, cmd: &Command,
capture: bool) -> CommandResult {
let tokens = cmd.tokens.clone();
let mut cr = CommandResult::new();
let args = parsers::parser_line::tokens_to_args(&tokens);

if args.len() > 2 {
println_stderr!("cicada: cd: too many argument");
return 1;
let info = "cicada: cd: too many argument";
print_stderr_with_capture(info, &mut cr, cl, cmd, capture);
return cr;
}

let str_current_dir = tools::get_current_dir();
Expand All @@ -26,44 +30,46 @@ pub fn run(sh: &mut shell::Shell, tokens: &Tokens) -> i32 {

if dir_to == "-" {
if sh.previous_dir == "" {
println_stderr!("no previous dir");
return 1;
let info = "no previous dir";
print_stderr_with_capture(info, &mut cr, cl, cmd, capture);
return cr;
}
dir_to = sh.previous_dir.clone();
} else if !dir_to.starts_with('/') {
dir_to = format!("{}/{}", str_current_dir, dir_to);
}

if !Path::new(&dir_to).exists() {
let info = format!("cicada: cd: {}: No such file or directory", &args[1]);
print_stderr_with_capture(&info, &mut cr, cl, cmd, capture);
return cr;
}

match Path::new(&dir_to).canonicalize() {
Ok(p) => {
dir_to = p.as_path().to_string_lossy().to_string();
}
Err(e) => {
println_stderr!("cicada: cd: error: {:?}", e);
return 1;
let info = format!("cicada: cd: error: {}", e);
print_stderr_with_capture(&info, &mut cr, cl, cmd, capture);
return cr;
}
}

if !Path::new(&dir_to).exists() {
println_stderr!(
"cicada: cd: {}: No such file or directory",
args[1..].join("")
);
return 1;
}

match env::set_current_dir(&dir_to) {
Ok(_) => {
sh.current_dir = dir_to.clone();
if str_current_dir != dir_to {
sh.previous_dir = str_current_dir.clone();
env::set_var("PWD", &sh.current_dir);
};
0
cr.status = 0;
cr
}
Err(e) => {
println_stderr!("cicada: cd: {}", e);
1
let info = format!("cicada: cd: {}", e);
print_stderr_with_capture(&info, &mut cr, cl, cmd, capture);
cr
}
}
}
14 changes: 11 additions & 3 deletions src/builtins/cinfo.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use crate::builtins::utils::print_stdout_with_capture;
use crate::history;
use crate::libs;
use crate::rcfile;
use crate::shell::Shell;
use crate::types::{Command, CommandLine, CommandResult};

pub fn run() -> i32 {
pub fn run(_sh: &mut Shell, cl: &CommandLine, cmd: &Command,
capture: bool) -> CommandResult {
let mut info = vec![];
const VERSION: &str = env!("CARGO_PKG_VERSION");
info.push(("version", VERSION));
Expand Down Expand Up @@ -35,9 +39,13 @@ pub fn run() -> i32 {
info.push(("built-with", env!("BUILD_RUSTC_VERSION")));
info.push(("built-at", env!("BUILD_DATE")));

let mut lines = Vec::new();
for (k, v) in &info {
// longest key above is 12-char length
println!("{: >12}: {}", k, v);
lines.push(format!("{: >12}: {}", k, v));
}
0
let buffer = lines.join("\n");
let mut cr = CommandResult::new();
print_stdout_with_capture(&buffer, &mut cr, cl, cmd, capture);
cr
}
Loading

0 comments on commit c324f16

Please sign in to comment.