Initial commit
This commit is contained in:
commit
c9b1b2fba9
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
*.pyc
|
52
commands.py
Normal file
52
commands.py
Normal file
@ -0,0 +1,52 @@
|
||||
from formatters import Printer
|
||||
from io import StringIO
|
||||
|
||||
|
||||
class BaseCommand(object):
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
raise NotImplementedError(
|
||||
"BaseCommands must be callable and return a generator")
|
||||
|
||||
|
||||
class NoneCommand(BaseCommand):
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return []
|
||||
|
||||
|
||||
|
||||
class RawCommand(BaseCommand):
|
||||
"""Fallback raw command that just invokes an existing Unix utility program
|
||||
with the builtin subprocess module. Each output object is just a tree node
|
||||
whose data is a simple string."""
|
||||
|
||||
def __init__(self, cmd, input_generator=[], args=tuple()):
|
||||
self.input_generator = input_generator
|
||||
self.cmd = cmd
|
||||
self.args = args
|
||||
|
||||
def __call__(self, input_generator=[], *args, **kwargs):
|
||||
import subprocess
|
||||
try:
|
||||
p = subprocess.Popen((self.cmd,) + self.args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
def output_generator():
|
||||
input_str = b""
|
||||
for line in input_generator:
|
||||
input_str += line
|
||||
outs, errs = p.communicate(input_str)
|
||||
if outs:
|
||||
yield outs
|
||||
|
||||
return output_generator()
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return []
|
||||
|
||||
|
||||
registered_cmds = []
|
||||
|
||||
def register_cmd(cls):
|
||||
registered_cmds.append(cls.__name__)
|
||||
return cls
|
39
example.typescript
Normal file
39
example.typescript
Normal file
@ -0,0 +1,39 @@
|
||||
Script started on Fri 20 Feb 2015 06:18:07 PM EST
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J
|
||||
(B[m[34m(B[m[32m[1mblazer(B[m[34m (B[m[34m[1m~/dev/mqp (B[m[34m(B[m[35m18:18 (B[m[34m
|
||||
(B[m(B[m [K[192C[31m[00m[192D[?1h=
[A[A[0m[27m[24m[J
|
||||
(B[m[34m(B[m[32m[1mblazer(B[m[34m (B[m[34m[1m~/dev/mqp (B[m[34m(B[m[35m18:18 (B[m[34m
|
||||
(B[m➤(B[m [K[191C[31m[00m[191D[32mscript[39m[32mp[32my[32mt[32mh[32mo[32mn[39m [4mmain.py[24m[?1l>
[A[A[0m[27m[24m[J
|
||||
(B[m[34m(B[m[32m[1mblazer(B[m[34m (B[m[34m[1m~/dev/mqp (B[m[34m(B[m[35m18:18 (B[m[34m
|
||||
(B[m➤(B[m [32mpython[39m [4mmain.py[24m[K[177C[31m[00m[177D
|
||||
Augmented Unix Userland
|
||||
>>> ls
|
||||
[DEBUG]: evaluating Python: Printer()(RawCommand('ls')())
|
||||
commands.py
|
||||
example_cmd.py
|
||||
formatters.py
|
||||
main.py
|
||||
__pycache__
|
||||
typescript
|
||||
|
||||
>>> ls | grep format
|
||||
[DEBUG]: evaluating Python: Printer()(RawCommand('grep', args=('format',))(RawCommand('ls')()))
|
||||
formatters.py
|
||||
|
||||
>>> >ls 2 + 2
|
||||
4
|
||||
>>> >ls )ou grep ls_outu put = ls()
|
||||
>>> >ls grep.args = (', py',)
|
||||
>>> >grepped_outpu p.args = ('format',)
|
||||
>>> >grepped_output = ls grep(ls_output)
|
||||
>>> >g Printer()(grepped_output)
|
||||
formatters.py
|
||||
|
||||
>>>
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J
|
||||
(B[m[34m(B[m[32m[1mblazer(B[m[34m (B[m[34m[1m~/dev/mqp (B[m[34m(B[m[35m18:19 (B[m[34m
|
||||
(B[m➤(B[m [K[191C[31m[00m[191D[?1h=
[A[A[0m[27m[24m[J
|
||||
(B[m[34m(B[m[32m[1mblazer(B[m[34m (B[m[34m[1m~/dev/mqp (B[m[34m(B[m[35m18:19 (B[m[34m
|
||||
(B[m➤(B[m [K[191C[31m[00m[191D
|
||||
|
||||
Script done on Fri 20 Feb 2015 06:19:26 PM EST
|
20
example_cmd.py
Normal file
20
example_cmd.py
Normal file
@ -0,0 +1,20 @@
|
||||
from commands import BaseCommand, register_cmd
|
||||
|
||||
@register_cmd
|
||||
class example_cmd(BaseCommand):
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def input_generator():
|
||||
yield 'example'
|
||||
yield 'command'
|
||||
return input_generator
|
||||
|
||||
|
||||
@register_cmd
|
||||
class echo(BaseCommand):
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def input_generator():
|
||||
for line in self.input_cmd():
|
||||
yield line
|
||||
return input_generator
|
23
formatters.py
Normal file
23
formatters.py
Normal file
@ -0,0 +1,23 @@
|
||||
class Formatter(object):
|
||||
"""Formatters are callable objects who always accept a generator.
|
||||
The generator represents the output stream of an executing program.
|
||||
Formatters are special commands which produce no output themselves.
|
||||
Instead, they always write to the standard out of the program. """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Always require no arguments
|
||||
pass
|
||||
|
||||
def __call__(self, input_generator):
|
||||
raise NotImplementedError(
|
||||
"You must extend a Formatter and implement the __call__ method")
|
||||
|
||||
|
||||
class Printer(Formatter):
|
||||
"""Simple formatter which accepts any object from the input
|
||||
generator and simply prints it as if it were a string."""
|
||||
|
||||
def __call__(self, input_generator):
|
||||
for line in input_generator:
|
||||
print(str(line.decode('utf-8')))
|
||||
return None
|
63
main.py
Normal file
63
main.py
Normal file
@ -0,0 +1,63 @@
|
||||
from commands import RawCommand, registered_cmds
|
||||
from formatters import Printer
|
||||
from example_cmd import example_cmd, echo
|
||||
|
||||
import os
|
||||
import os.path
|
||||
for path in os.environ['PATH'].split(':'):
|
||||
if os.path.exists(path):
|
||||
binaries = os.listdir(path)
|
||||
for binary in binaries:
|
||||
globals()[binary] = RawCommand(binary)
|
||||
|
||||
|
||||
def parse_cmd(potential_cmd):
|
||||
"""Evaluates a potential command. If it exists in the list of
|
||||
registered commands, we return a string that would call the
|
||||
constructor for that command. If it does not exist, we wrap the
|
||||
name of the command with the RawCommand class.
|
||||
|
||||
:return: A string that when evaluated by Python's `eval` feature
|
||||
would build an object of the correct type
|
||||
"""
|
||||
args = potential_cmd.strip().split(' ')
|
||||
cmd_name = args[0]
|
||||
if args:
|
||||
args = args[1:]
|
||||
if cmd_name not in registered_cmds:
|
||||
if args:
|
||||
return "RawCommand('{}', args={})".format(cmd_name, str(tuple(args)))
|
||||
else:
|
||||
return "RawCommand('{}')".format(cmd_name)
|
||||
else:
|
||||
return "{}()".format(cmd_name)
|
||||
|
||||
|
||||
def parse_cmds(raw):
|
||||
potential_cmds = raw.split('|')
|
||||
cmds = [parse_cmd(cmd) for cmd in potential_cmds]
|
||||
return cmds
|
||||
|
||||
|
||||
def handle_input(prompt=""):
|
||||
raw = input(prompt)
|
||||
if raw[0] == '>':
|
||||
return raw[1:]
|
||||
else:
|
||||
cmds = parse_cmds(raw)
|
||||
cmds.append("Printer()")
|
||||
mangled_input = ""
|
||||
for cmd in reversed(cmds):
|
||||
mangled_input += cmd + "("
|
||||
mangled_input += ")" * len(cmds)
|
||||
print("[DEBUG]: evaluating Python: ", mangled_input)
|
||||
return mangled_input
|
||||
|
||||
|
||||
def main():
|
||||
import code
|
||||
code.interact("Augmented Unix Userland", handle_input, globals())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Reference in New Issue
Block a user