mirror of
https://github.com/samsonjs/csc360-a1-shell.git
synced 2026-03-25 08:45:52 +00:00
Harden parser error handling and builtin usage checks
This commit is contained in:
parent
c94e4c87e2
commit
058a2e991f
5 changed files with 51 additions and 2 deletions
|
|
@ -24,6 +24,11 @@ module Shell
|
|||
#################
|
||||
|
||||
def builtin_bg(args)
|
||||
if args.empty?
|
||||
logger.warn "Usage: bg <command>"
|
||||
return -1
|
||||
end
|
||||
|
||||
cmd = args.shift
|
||||
job_control.exec_command(cmd, args, background: true)
|
||||
end
|
||||
|
|
@ -67,10 +72,16 @@ module Shell
|
|||
end
|
||||
|
||||
def builtin_export(args)
|
||||
if args.count != 1 || args.first.nil? || !args.first.include?("=")
|
||||
logger.warn "Usage: export NAME=value"
|
||||
return -1
|
||||
end
|
||||
|
||||
# only supports one variable and doesn't support quoting
|
||||
name, *value_parts = args.first.strip.split("=")
|
||||
if name.nil? || name.empty?
|
||||
logger.warn "#{red("[ERROR]")} Invalid export command"
|
||||
return -1
|
||||
else
|
||||
ENV[name] = value_parts.join("=").gsub(/\$\w+/) { |m| ENV[m[1..]] || "" }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ module Shell
|
|||
if options[:command]
|
||||
logger.verbose "Executing command: #{options[:command]}"
|
||||
print_logs
|
||||
exit repl.process_command(options[:command])
|
||||
status = repl.process_command(options[:command])
|
||||
print_logs
|
||||
exit status
|
||||
elsif $stdin.isatty
|
||||
repl.start(options: options)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ module Shell
|
|||
end
|
||||
end
|
||||
result
|
||||
rescue Errno => e
|
||||
rescue StandardError => e
|
||||
warn "#{red("[ERROR]")} #{e.message}"
|
||||
-1
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ module Shell
|
|||
next
|
||||
when "&"
|
||||
if line[i + 1] == "&"
|
||||
if command.strip.empty?
|
||||
raise ArgumentError, "syntax error near unexpected token `&&`"
|
||||
end
|
||||
commands << {command: command, op: next_op}
|
||||
command = +""
|
||||
next_op = :and
|
||||
|
|
@ -63,6 +66,10 @@ module Shell
|
|||
i += 1
|
||||
end
|
||||
|
||||
if next_op == :and && command.strip.empty?
|
||||
raise ArgumentError, "syntax error: expected command after `&&`"
|
||||
end
|
||||
|
||||
commands << {command: command, op: next_op}
|
||||
commands
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
require "minitest/autorun"
|
||||
require "etc"
|
||||
require "open3"
|
||||
require "timeout"
|
||||
$LOAD_PATH.unshift(File.expand_path("..", __dir__))
|
||||
require_relative "../shell/job_control"
|
||||
|
|
@ -130,6 +131,34 @@ class ShellTest < Minitest::Test
|
|||
assert_equal "`echo hi`", %x(#{A1_PATH} -c 'echo "\\`echo hi\\`"').chomp
|
||||
end
|
||||
|
||||
def test_reports_parse_errors_without_ruby_backtrace
|
||||
_stdout, stderr, status = Open3.capture3(A1_PATH, "-c", "echo \"unterminated")
|
||||
refute status.success?
|
||||
refute_match(/\.rb:\d+:in /, stderr)
|
||||
end
|
||||
|
||||
def test_export_without_args_does_not_raise_nomethoderror
|
||||
_stdout, stderr, status = Open3.capture3(A1_PATH, "-c", "export")
|
||||
refute status.success?
|
||||
refute_match(/NoMethodError|undefined method/, stderr)
|
||||
end
|
||||
|
||||
def test_bg_without_command_reports_usage_error
|
||||
_stdout, stderr, status = Open3.capture3(A1_PATH, "-c", "bg")
|
||||
refute status.success?
|
||||
assert_match(/Usage: bg <command>/, stderr)
|
||||
end
|
||||
|
||||
def test_rejects_empty_command_around_and_operator
|
||||
_stdout1, stderr1, status1 = Open3.capture3(A1_PATH, "-c", "&& echo hi")
|
||||
refute status1.success?
|
||||
assert_match(/syntax/i, stderr1)
|
||||
|
||||
_stdout2, stderr2, status2 = Open3.capture3(A1_PATH, "-c", "echo hi &&")
|
||||
refute status2.success?
|
||||
assert_match(/syntax/i, stderr2)
|
||||
end
|
||||
|
||||
#################################
|
||||
### Execution and job control ###
|
||||
#################################
|
||||
|
|
|
|||
Loading…
Reference in a new issue