summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Gayot <olivier.gayot@sigexec.com>2020-03-21 00:06:56 +0100
committerOlivier Gayot <olivier.gayot@sigexec.com>2020-03-21 12:42:21 +0100
commit3da7ccbf94fc9dfeead62b0091b0f365bf89c0ef (patch)
tree72e501822223198f83360c3ad3096ba73fc60217
parent609fad38b5818806323757d816d9fac5ba3c8a4c (diff)
Handle quoting, escapements and IFS correctly
Signed-off-by: Olivier Gayot <olivier.gayot@sigexec.com>
-rwxr-xr-xvish.py109
1 files changed, 98 insertions, 11 deletions
diff --git a/vish.py b/vish.py
index a8febc3..ec601a0 100755
--- a/vish.py
+++ b/vish.py
@@ -4,28 +4,115 @@ import os
import re
import argparse
+IFS = (" ", "\t", "\n")
+
+
class EOFException(Exception):
pass
+class Argument():
+ def __init__(self, value: str, allow_expansion=True):
+ pass
+
+ def expand():
+ pass
+
+
+class Pipeline():
+ def __init__(self, instructions: list=[]):
+ self.instructions = instructions
+
+ def execute(self):
+ for instruction in self.instructions:
+ instruction.execute()
+
+
class Instruction():
- def __init__(self, string: str):
- # TODO handle quoting and escapments
- keywords = string.split()
- self.prog = keywords[0]
- self.arguments = keywords[1:]
+ def __init__(self, tokens: list=[]):
+ self.prog = tokens[0]
+ self.args = tokens[1:]
def execute(self):
cpid = os.fork()
if cpid == 0:
- os.execvp(self.prog, [self.prog] + self.arguments)
+ os.execvp(self.prog, [self.prog] + self.args)
else:
os.waitpid(cpid, 0)
-def read_instruction(fh):
- # TODO We should be reading an instruction, not a line.
+class PipelineParser():
+ def read_escaped(self, string_iterator):
+ return next(string_iterator)
+
+ def read_literal(self, string_iterator):
+ literal = ""
+ while True:
+ c = next(string_iterator)
+ if c != "'":
+ literal += c
+ else:
+ return literal
+
+ def read_quoted(self, string_iterator):
+ # TODO handle substitutions
+ quoted = ""
+ while True:
+ c = next(string_iterator)
+ if c == "\\":
+ quoted += self.read_escaped(string_iterator)
+ elif c != '"':
+ quoted += c
+ else:
+ return quoted
+
+
+ def get_next_token(self, string_iterator):
+ token = None
+ while True:
+ try:
+ c = next(string_iterator)
+ except StopIteration:
+ break
+
+ # Skip leading whitespaces
+ if token is None and c in IFS:
+ continue
+ elif token is None:
+ token = ""
+
+ if c == "'":
+ token += self.read_literal(string_iterator)
+ elif c == "\\":
+ token += self.read_escaped(string_iterator)
+ elif c == "\"":
+ token += self.read_quoted(string_iterator)
+ elif c in IFS:
+ return token
+ else:
+ token += c
+
+ return token
+
+ def tokenize(self, string_iterator):
+ while True:
+ token = self.get_next_token(string_iterator)
+ if token is None:
+ break
+ yield token
+
+ def parse(self, line):
+ tokens = list(self.tokenize(iter(line)))
+ return Pipeline([Instruction(tokens)])
+
+
+def read_next_pipeline(fh):
+ # TODO Support multiple pipelines per line
+ # TODO Support instructions spawning multiple lines
+
+ parser = PipelineParser()
+
while True:
line = fh.readline()
if line == "":
@@ -36,7 +123,7 @@ def read_instruction(fh):
if not line.startswith("#") and not line == "":
break
- return Instruction(line)
+ return parser.parse(line)
def main(arguments):
@@ -44,8 +131,8 @@ def main(arguments):
with open(script, mode="r", encoding="utf-8") as fh:
while True:
try:
- instruction = read_instruction(fh)
- instruction.execute()
+ pipeline = read_next_pipeline(fh)
+ pipeline.execute()
except EOFException:
break