Browse Source

Initial commit

Ian Adam Naval 4 years ago
commit
c9b1b2fba9
6 changed files with 199 additions and 0 deletions
  1. 2
    0
      .gitignore
  2. 52
    0
      commands.py
  3. 39
    0
      example.typescript
  4. 20
    0
      example_cmd.py
  5. 23
    0
      formatters.py
  6. 63
    0
      main.py

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
1
+__pycache__
2
+*.pyc

+ 52
- 0
commands.py View File

@@ -0,0 +1,52 @@
1
+from formatters import Printer
2
+from io import StringIO
3
+
4
+
5
+class BaseCommand(object):
6
+
7
+    def __call__(self, *args, **kwargs):
8
+        raise NotImplementedError(
9
+            "BaseCommands must be callable and return a generator")
10
+
11
+
12
+class NoneCommand(BaseCommand):
13
+
14
+    def __call__(self, *args, **kwargs):
15
+        return []
16
+
17
+
18
+
19
+class RawCommand(BaseCommand):
20
+    """Fallback raw command that just invokes an existing Unix utility program
21
+    with the builtin subprocess module. Each output object is just a tree node
22
+    whose data is a simple string."""
23
+
24
+    def __init__(self, cmd, input_generator=[], args=tuple()):
25
+        self.input_generator = input_generator
26
+        self.cmd = cmd
27
+        self.args = args
28
+
29
+    def __call__(self, input_generator=[], *args, **kwargs):
30
+        import subprocess
31
+        try:
32
+            p = subprocess.Popen((self.cmd,) + self.args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
33
+            def output_generator():
34
+                input_str = b""
35
+                for line in input_generator:
36
+                    input_str += line
37
+                outs, errs = p.communicate(input_str)
38
+                if outs:
39
+                    yield outs
40
+
41
+            return output_generator()
42
+        except:
43
+            import traceback
44
+            traceback.print_exc()
45
+        return []
46
+
47
+
48
+registered_cmds = []
49
+
50
+def register_cmd(cls):
51
+    registered_cmds.append(cls.__name__)
52
+    return cls

+ 39
- 0
example.typescript View File

@@ -0,0 +1,39 @@
1
+Script started on Fri 20 Feb 2015 06:18:07 PM EST
2
+%                                                                                                                                                                                                 
 


3
+(B(Bblazer(B (B~/dev/mqp (B(B18:18 (B
4
+(B(B [?1h=


5
+(B(Bblazer(B (B~/dev/mqp (B(B18:18 (B
6
+(B➤(B scriptpython main.py[?1l>


7
+(B(Bblazer(B (B~/dev/mqp (B(B18:18 (B
8
+(B➤(B python main.py

9
+Augmented Unix Userland
10
+>>> ls
11
+[DEBUG]: evaluating Python:  Printer()(RawCommand('ls')())
12
+commands.py
13
+example_cmd.py
14
+formatters.py
15
+main.py
16
+__pycache__
17
+typescript
18
+
19
+>>> ls | grep format
20
+[DEBUG]: evaluating Python:  Printer()(RawCommand('grep', args=('format',))(RawCommand('ls')()))
21
+formatters.py
22
+
23
+>>> >ls  2 + 2
24
+4
25
+>>> >ls  )ou     grep    ls_outu put = ls()
26
+>>> >ls  grep.args = (', py',)
27
+>>> >grepped_outpu          p.args = ('format',)
28
+>>> >grepped_output = ls  grep(ls_output)
29
+>>> >g Printer()(grepped_output)
30
+formatters.py
31
+
32
+>>> 
33
+%                                                                                                                                                                                                 
 


34
+(B(Bblazer(B (B~/dev/mqp (B(B18:19 (B
35
+(B➤(B [?1h=


36
+(B(Bblazer(B (B~/dev/mqp (B(B18:19 (B
37
+(B➤(B 

38
+
39
+Script done on Fri 20 Feb 2015 06:19:26 PM EST

+ 20
- 0
example_cmd.py View File

@@ -0,0 +1,20 @@
1
+from commands import BaseCommand, register_cmd
2
+
3
+@register_cmd
4
+class example_cmd(BaseCommand):
5
+
6
+	def __call__(self, *args, **kwargs):
7
+		def input_generator():
8
+			yield 'example'
9
+			yield 'command'
10
+		return input_generator
11
+
12
+
13
+@register_cmd
14
+class echo(BaseCommand):
15
+
16
+	def __call__(self, *args, **kwargs):
17
+		def input_generator():
18
+			for line in self.input_cmd():
19
+				yield line
20
+		return input_generator

+ 23
- 0
formatters.py View File

@@ -0,0 +1,23 @@
1
+class Formatter(object):
2
+	"""Formatters are callable objects who always accept a generator.
3
+	The generator represents the output stream of an executing program.
4
+	Formatters are special commands which produce no output themselves.
5
+	Instead, they always write to the standard out of the program. """
6
+
7
+	def __init__(self, *args, **kwargs):
8
+		# Always require no arguments
9
+		pass
10
+
11
+	def __call__(self, input_generator):
12
+		raise NotImplementedError(
13
+			"You must extend a Formatter and implement the __call__ method")
14
+
15
+
16
+class Printer(Formatter):
17
+	"""Simple formatter which accepts any object from the input
18
+	generator and simply prints it as if it were a string."""
19
+
20
+	def __call__(self, input_generator):
21
+		for line in input_generator:
22
+			print(str(line.decode('utf-8')))
23
+		return None

+ 63
- 0
main.py View File

@@ -0,0 +1,63 @@
1
+from commands import RawCommand, registered_cmds
2
+from formatters import Printer
3
+from example_cmd import example_cmd, echo
4
+
5
+import os
6
+import os.path
7
+for path in os.environ['PATH'].split(':'):
8
+    if os.path.exists(path):
9
+        binaries = os.listdir(path)
10
+        for binary in binaries:
11
+            globals()[binary] = RawCommand(binary)
12
+
13
+
14
+def parse_cmd(potential_cmd):
15
+    """Evaluates a potential command. If it exists in the list of
16
+    registered commands, we return a string that would call the
17
+    constructor for that command. If it does not exist, we wrap the
18
+    name of the command with the RawCommand class.
19
+
20
+    :return: A string that when evaluated by Python's `eval` feature
21
+        would build an object of the correct type
22
+    """
23
+    args = potential_cmd.strip().split(' ')
24
+    cmd_name = args[0]
25
+    if args:
26
+        args = args[1:]
27
+    if cmd_name not in registered_cmds:
28
+        if args:
29
+            return "RawCommand('{}', args={})".format(cmd_name, str(tuple(args)))
30
+        else:
31
+            return "RawCommand('{}')".format(cmd_name)
32
+    else:
33
+        return "{}()".format(cmd_name)
34
+
35
+
36
+def parse_cmds(raw):
37
+    potential_cmds = raw.split('|')
38
+    cmds = [parse_cmd(cmd) for cmd in potential_cmds]
39
+    return cmds
40
+
41
+
42
+def handle_input(prompt=""):
43
+    raw = input(prompt)
44
+    if raw[0] == '>':
45
+        return raw[1:]
46
+    else:
47
+        cmds = parse_cmds(raw)
48
+        cmds.append("Printer()")
49
+        mangled_input = ""
50
+        for cmd in reversed(cmds):
51
+            mangled_input += cmd + "("
52
+        mangled_input += ")" * len(cmds)
53
+        print("[DEBUG]: evaluating Python: ", mangled_input)
54
+        return mangled_input
55
+
56
+
57
+def main():
58
+    import code
59
+    code.interact("Augmented Unix Userland", handle_input, globals())
60
+
61
+
62
+if __name__ == '__main__':
63
+    main()

Loading…
Cancel
Save