Skip to content

Instantly share code, notes, and snippets.

@nixpulvis nixpulvis/shell.rb
Last active Oct 13, 2018

Embed
What would you like to do?
# Commands to be executed directly by this shell.
BUILTINS = {}
# The builtin `cd` for changing the shell's working directory.
BUILTINS['cd'] = lambda do |args|
# Change to the home directory by default.
args << ENV['HOME'] if args.empty?
# Read the destination path, doing very basic path expansion.
dest = args.pop.gsub(/~/, ENV['HOME'])
# Try to change this shell's working directory.
begin
Dir.chdir(dest)
rescue Exception
puts("no such directory: #{dest}")
end
end
# The builtin to exit the shell.
BUILTINS['exit'] = lambda do |args|
# Exit with a status of 0 by default.
args << 0 if args.empty?
# Exit the shell.
exit args.pop.to_i
end
# Print a prompt, then read a line from STDIN.
def read
print('$ ')
# Try to read a line, exiting if there's no more lines.
line = STDIN.gets
if line
line.chomp.split(' ')
else
exit
end
end
# Run the given command in a subprocess.
def evaluate(argv)
if BUILTINS[argv[0]]
BUILTINS[argv[0]].call(argv[1..-1])
else
success = system(*argv)
unless success
puts("unknown command '#{argv[0]}'")
end
end
end
# Don't exit on `SIGINT`, instead simply return a new prompt.
trap('INT') { print("\n$ ") }
# The glorious REPL itself.
loop { evaluate(read) }
use std::io::{self, Read, Write};
use std::process::{Command, Output};
use std::str;
// Our shell, for the greater good. Ready and waiting.
fn main() {
// Standard input file descriptor (0), used for user input from the user
// of the shell.
let mut stdin = io::stdin();
// Standard output file descriptor (1), used to display program output to
// the user of the shell.
let mut stdout = io::stdout();
// STDIN, input buffer, used to `read` text into for further processing.
// TODO: Full fledged parser will be neato.
let mut input = [0; 24];
loop {
// XXX: Blindly drop the contents of input, again this will be better
// with a real parser.
input = [0; 24];
// Print a boring static prompt.
print!("oursh> ");
stdout.lock().flush();
loop {
// TODO: Enable raw access to STDIN, so we can read as the user
// types. By default the underlying file is line buffered. This
// will allow us to process history, syntax, and more!
// Read what's avalible to us.
stdin.read(&mut input).expect("error reading STDIN");
// Once we've read a complete "program" (§2.10.2) we handle it,
// until then we keep reading. Once we have a proper parser this
// wont look like a huge hack.
let vec: Vec<&[u8]> = input.splitn(2, |b| *b == '\n' as u8).collect();
match &vec[..] {
[line, rest] => {
let program = str::from_utf8(&line).expect("error reading utf8");
let mut output = handle_program(program);
stdout.write(&output.stdout).expect("error writing to STDOUT");
break
}
_ => {},
}
}
}
}
// Obviously very wrong. Most notably this blocks until the command completes.
fn handle_program(program: &str) -> Output {
Command::new(program)
.output()
.expect("error executing program")
}
@nixpulvis

This comment has been minimized.

Copy link
Owner Author

nixpulvis commented Jul 19, 2018

Rust / Ruby Filesize equivalent.

@nixpulvis

This comment has been minimized.

Copy link
Owner Author

nixpulvis commented Oct 13, 2018

oursh progress

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Rust                             7            100            478            594
-------------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.