mirror of
https://github.com/samsonjs/csc360-a1-shell.git
synced 2026-04-27 14:57:43 +00:00
Perform proper shell word splitting and improve logging
This commit is contained in:
parent
01b0983f79
commit
9faa33fbf2
3 changed files with 39 additions and 32 deletions
|
|
@ -2,7 +2,7 @@ class Shell
|
||||||
class Builtins
|
class Builtins
|
||||||
attr_reader :logger
|
attr_reader :logger
|
||||||
|
|
||||||
def initialize(logger)
|
def initialize(logger = ShellLogger.instance)
|
||||||
@logger = logger
|
@logger = logger
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
53
ruby/main.rb
53
ruby/main.rb
|
|
@ -3,6 +3,7 @@
|
||||||
require 'English'
|
require 'English'
|
||||||
require 'open3'
|
require 'open3'
|
||||||
require 'readline'
|
require 'readline'
|
||||||
|
require 'shellwords'
|
||||||
|
|
||||||
require './builtins'
|
require './builtins'
|
||||||
require './colours'
|
require './colours'
|
||||||
|
|
@ -12,12 +13,12 @@ require './job'
|
||||||
class Shell
|
class Shell
|
||||||
attr_reader :logger, :options
|
attr_reader :logger, :options
|
||||||
|
|
||||||
def initialize(args)
|
def initialize(args = ARGV)
|
||||||
@logger = ShellLogger.new
|
@builtins = Builtins.new
|
||||||
@options = parse_options(args)
|
|
||||||
@jobs_by_pid = {}
|
@jobs_by_pid = {}
|
||||||
@builtins = Builtins.new(@logger)
|
@logger = ShellLogger.instance
|
||||||
logger.verbose "options: #{options.inspect}"
|
@options = parse_options(args)
|
||||||
|
logger.verbose "Options: #{options.inspect}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def main
|
def main
|
||||||
|
|
@ -26,13 +27,13 @@ class Shell
|
||||||
if options[:command]
|
if options[:command]
|
||||||
logger.verbose "Executing command: #{options[:command]}"
|
logger.verbose "Executing command: #{options[:command]}"
|
||||||
print_logs
|
print_logs
|
||||||
exit exec_command(options[:command])
|
exit process_command(options[:command])
|
||||||
elsif $stdin.isatty
|
elsif $stdin.isatty
|
||||||
add_to_history = true
|
add_to_history = true
|
||||||
loop do
|
loop do
|
||||||
print_logs
|
print_logs
|
||||||
cmd = Readline.readline(prompt(Dir.pwd), add_to_history)
|
line = Readline.readline(prompt(Dir.pwd), add_to_history)
|
||||||
process_command(cmd)
|
process_command(line)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -78,39 +79,35 @@ class Shell
|
||||||
message = "#{log.message}#{CLEAR}"
|
message = "#{log.message}#{CLEAR}"
|
||||||
case log.level
|
case log.level
|
||||||
when :verbose
|
when :verbose
|
||||||
puts message if options[:verbose]
|
warn message if options[:verbose]
|
||||||
when :warning
|
|
||||||
warn message
|
|
||||||
else
|
else
|
||||||
puts message
|
warn message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
logger.clear
|
logger.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_command(cmd)
|
def process_command(line)
|
||||||
logger.verbose "Processing command: #{cmd.inspect}"
|
logger.verbose "Processing command: #{line.inspect}"
|
||||||
exit 0 if cmd.nil? # EOF, ctrl-d
|
exit 0 if line.nil? # EOF, ctrl-d
|
||||||
return if cmd.empty? # no input, no-op
|
return if line.empty? # no input, no-op
|
||||||
|
|
||||||
# TODO: proper word splitting, pass arrays to built-ins
|
args = Shellwords.split(line)
|
||||||
args = cmd.split
|
cmd = args.shift
|
||||||
argv0 = args.first
|
logger.verbose "Words: #{cmd} #{args.inspect}"
|
||||||
logger.verbose "\"Words\": #{args.inspect}"
|
if @builtins.builtin?(cmd)
|
||||||
if @builtins.builtin?(argv0)
|
logger.verbose "Executing builtin #{cmd}"
|
||||||
logger.verbose "Executing builtin #{argv0}"
|
@builtins.exec(cmd, args)
|
||||||
args.shift
|
|
||||||
@builtins.exec(argv0, args)
|
|
||||||
else
|
else
|
||||||
logger.verbose "Shelling out for #{argv0}"
|
logger.verbose "Shelling out for #{cmd}"
|
||||||
status = exec_command(cmd)
|
status = exec_command(cmd, args)
|
||||||
print "#{RED}-#{status}-#{CLEAR} " unless status.zero?
|
print "#{RED}-#{status}-#{CLEAR} " unless status.zero?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def exec_command(cmd)
|
def exec_command(cmd, args)
|
||||||
# TODO: background execution using fork + exec, streaming output
|
# TODO: background execution using fork + exec, streaming output
|
||||||
out, err, status = Open3.capture3(cmd)
|
out, err, status = Open3.capture3(cmd + ' ' + args.join(' '))
|
||||||
puts out.chomp unless out.empty?
|
puts out.chomp unless out.empty?
|
||||||
warn err.chomp unless err.empty?
|
warn err.chomp unless err.empty?
|
||||||
status.exitstatus
|
status.exitstatus
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
require './colours'
|
||||||
|
|
||||||
# Queues up messages to be printed out when readline is waiting for input, to prevent
|
# Queues up messages to be printed out when readline is waiting for input, to prevent
|
||||||
# mixing shell output with command output.
|
# mixing shell output with command output.
|
||||||
class ShellLogger
|
class ShellLogger
|
||||||
|
|
@ -5,21 +7,29 @@ class ShellLogger
|
||||||
|
|
||||||
attr_reader :logs
|
attr_reader :logs
|
||||||
|
|
||||||
|
def self.instance
|
||||||
|
@instance ||= new
|
||||||
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
clear
|
clear
|
||||||
end
|
end
|
||||||
|
|
||||||
def log(message)
|
def log(message)
|
||||||
@logs << Log.new(:info, message)
|
@logs << Log.new(:info, "#{WHITE}[INFO]#{CLEAR} #{message}")
|
||||||
end
|
end
|
||||||
alias info log
|
alias info log
|
||||||
|
|
||||||
def warn(message)
|
def warn(message)
|
||||||
@logs << Log.new(:warning, message)
|
@logs << Log.new(:warning, "#{YELLOW}[WARN]#{CLEAR} #{message}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(message)
|
||||||
|
@logs << Log.new(:error, "#{RED}[ERROR]#{CLEAR} #{message}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def verbose(message)
|
def verbose(message)
|
||||||
@logs << Log.new(:verbose, message)
|
@logs << Log.new(:verbose, "[VERBOSE] #{message}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear
|
def clear
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue