Permalink
Browse files

implement redirection

  • Loading branch information...
1 parent 6862c8a commit 0c037b5c4b44ef38057beee722ae716abd2104f0 @nonylene committed Jan 1, 2017
Showing with 102 additions and 20 deletions.
  1. +102 −20 nsh
View
122 nsh
@@ -3,15 +3,25 @@ import sys
import os
import signal
+class ParseError(Exception):
+ pass
+
+class Command():
+ def __init__(self, args, redirect_in, redirect_out):
+ self.args = args
+ self.redirect_in = redirect_in
+ self.redirect_out = redirect_out
+
# do not ignore SIGPIPE
signal.signal(signal.SIGPIPE,signal.SIG_DFL)
def main():
while True:
line = input(os.getcwd() + ' $ ')
- commands = parse_line(line)
- if [] in commands:
- print("no command")
+ try:
+ commands = parse_line(line)
+ except ParseError as e:
+ print("nsh: {0}".format(e))
continue
execute_commands(commands)
@@ -24,13 +34,15 @@ def execute_commands(commands):
break
if len(commands) == 1:
- args = commands[0]
+ command = commands[0]
+ args = command.args
if args[0] in builtin_functions:
+ # run builtin on current process
builtin_functions[args[0]](args)
else:
pid = os.fork()
if pid == 0:
- os.execvp(args[0], args)
+ execvp_command(command)
elif pid < 0:
print("error with fork!")
else:
@@ -44,12 +56,26 @@ def execute_commands(commands):
else:
wait_child(pid)
-def execute_args(args, exit_builtin = False):
+def execute_command(command, exit_builtin = False):
+ args = command.args
if args[0] in builtin_functions:
builtin_functions[args[0]](args)
if exit_builtin: exit()
else:
- os.execvp(args[0], args)
+ execvp_command(command)
+
+def execvp_command(command):
+ args = command.args
+ if command.redirect_in:
+ infd = os.open(command.redirect_in, os.O_RDONLY)
+ os.dup2(infd, 0)
+ os.close(infd)
+ if command.redirect_out:
+ outfd = os.open(
+ command.redirect_out, os.O_WRONLY | os.O_CREAT, 0o644)
+ os.dup2(outfd, 1)
+ os.close(outfd)
+ os.execvp(args[0], args)
def execute_pipe(commands):
# use as queue
@@ -65,7 +91,7 @@ def execute_pipe(commands):
if len(commands) == 1:
# last
args = commands[0]
- execute_args(args, True)
+ execute_command(args, True)
else:
execute_pipe(commands)
elif pid < 0:
@@ -75,11 +101,14 @@ def execute_pipe(commands):
os.close(w)
os.dup2(r, 0)
os.close(r)
- execute_args(current_args, True)
+ execute_command(current_args, True)
# TODO: use AST?
def parse_line(line):
commands = []
+
+ redirect_in = None
+ redirect_out = None
args = []
current_arg = str()
@@ -89,54 +118,107 @@ def parse_line(line):
single_quote = False
# '\'
escaping = False
+ # '>'
+ redirecting_out = False
+ # '<'
+ redirecting_in = False
- def append_arg(allow_empty):
+ def append_cmd():
+ append_arg(False)
+ nonlocal redirect_in
+ nonlocal redirect_out
+ nonlocal commands
+ nonlocal args
+ # check
+ if redirect_in == str():
+ raise ParseError("empty redirect in")
+ if redirect_out == str():
+ raise ParseError("empty redirect out")
+ if not args:
+ raise ParseError("empty command")
+ commands.append(Command(args, redirect_in, redirect_out))
+ redirect_in = None
+ redirect_out = None
+ args = []
+
+ def append_arg(allow_empty, end_redirects = True):
# modify variable
+ if end_redirects:
+ nonlocal redirecting_in
+ redirecting_in = False
+ nonlocal redirecting_out
+ redirecting_out = False
nonlocal current_arg
if allow_empty or current_arg: args.append(current_arg)
current_arg = str()
+ def append_char(char):
+ if redirecting_in:
+ nonlocal redirect_in
+ redirect_in += char
+ elif redirecting_out:
+ nonlocal redirect_out
+ redirect_out += char
+ else:
+ nonlocal current_arg
+ current_arg += char
+
for s in line:
if s == '\n':
append_arg(False)
elif escaping:
escaping = False
- current_arg += s
+ append_char(s)
elif s == '\\':
escaping = True
elif s == '"':
if double_quote:
double_quote = False
append_arg(True)
elif single_quote:
- current_arg += s
+ append_char(s)
else:
double_quote = True
elif s == "'":
if single_quote:
single_quote = False
append_arg(True)
elif double_quote:
- current_arg += s
+ append_char(s)
else:
single_quote = True
single_quote = True
elif s in [" ", "\t"]:
if not (single_quote or double_quote or escaping):
+ append_arg(False, False)
+ else:
+ append_char(s)
+ elif s == ">":
+ if not (single_quote or double_quote or escaping):
+ if redirect_out: raise ParseError("multiple redirect out")
append_arg(False)
+ redirecting_out = True
+ redirect_out = str()
else:
- current_arg += s
- elif s == "|":
+ append_char(s)
+ elif s == "<":
if not (single_quote or double_quote or escaping):
+ if redirect_in: raise ParseError("multiple redirect in")
append_arg(False)
- commands.append(args)
- args = []
+ redirecting_in = True
+ redirect_in = str()
else:
- current_arg += s
+ append_char(s)
+ elif s == "|":
+ if not (single_quote or double_quote or escaping):
+ append_cmd()
+ else:
+ append_char(s)
else:
- current_arg += s
+ append_char(s)
+
append_arg(False)
- commands.append(args)
+ append_cmd()
return commands
def builtin_cd(args):

0 comments on commit 0c037b5

Please sign in to comment.