commit 88bddc5de5d1b7bec947a6780a5874dadd723128
Author: Damian Johnson <atagar(a)torproject.org>
Date: Fri Aug 26 10:28:15 2011 -0700
Porting basic interpretor functionality to prompt
Quick hack so the panel and prompt both share the same interpretor
input/response logic. This makes the arm prompt now basically functional though
gonna need to do some cleanup.
---
src/cli/interpretorPanel.py | 160 +++++++++----------------------------------
src/util/torInterpretor.py | 132 +++++++++++++++++++++++++++++++++++-
2 files changed, 163 insertions(+), 129 deletions(-)
diff --git a/src/cli/interpretorPanel.py b/src/cli/interpretorPanel.py
index b7c3fe0..b81fa85 100644
--- a/src/cli/interpretorPanel.py
+++ b/src/cli/interpretorPanel.py
@@ -5,13 +5,10 @@ information, tab completion, and other usability features.
import curses
-from util import enum, panel, textInput, torTools, uiTools
+from util import enum, panel, textInput, torInterpretor, torTools, uiTools
from TorCtl import TorCtl
-Formats = enum.Enum("PROMPT", "INPUT", "INPUT_INTERPRETOR", "INPUT_CMD", "INPUT_ARG", "OUTPUT", "USAGE", "HELP", "ERROR")
-
-PROMPT = ">>> "
USAGE_INFO = "to use this panel press enter"
# limits used for cropping
@@ -29,7 +26,7 @@ class InterpretorPanel(panel.Panel):
# contents of the panel (oldest to newest), each line is a list of (msg,
# format enum) tuples
- self.contents = [[(PROMPT, Formats.PROMPT), (USAGE_INFO, Formats.USAGE)]]
+ self.contents = [[(torInterpretor.PROMPT, torInterpretor.Formats.PROMPT), (USAGE_INFO, torInterpretor.Formats.USAGE)]]
def prompt(self):
"""
@@ -49,13 +46,32 @@ class InterpretorPanel(panel.Panel):
validator = textInput.BasicValidator()
validator = textInput.HistoryValidator(self.previousCommands, validator)
- xOffset = len(PROMPT)
+ xOffset = len(torInterpretor.PROMPT)
if len(self.contents) > self.maxY - 1:
xOffset += 3 # offset for scrollbar
- input = self.getstr(min(self.maxY - 1, len(self.contents)), xOffset, "", self.formats[Formats.INPUT], validator = validator)
+ input = self.getstr(min(self.maxY - 1, len(self.contents)), xOffset, "", self.formats[torInterpretor.Formats.INPUT], validator = validator)
+ input, isDone = input.strip(), False
- isDone = self.handleQuery(input)
+ if not input:
+ isDone = True
+ else:
+ self.previousCommands.insert(0, input)
+ self.previousCommands = self.previousCommands[:COMMAND_BACKLOG]
+
+ try:
+ inputEntry, outputEntry = torInterpretor.handleQuery(input)
+ except torInterpretor.InterpretorClosed:
+ isDone = True
+
+ promptEntry = self.contents.pop() # removes old prompt entry
+ self.contents += inputEntry
+ self.contents += outputEntry
+ self.contents.append(promptEntry)
+
+ # if too long then crop lines
+ cropLines = len(self.contents) - LINES_BACKLOG
+ if cropLines > 0: self.contents = self.contents[cropLines:]
if isDone:
self.isInputMode = False
@@ -63,89 +79,6 @@ class InterpretorPanel(panel.Panel):
panel.CURSES_LOCK.release()
- def handleQuery(self, input):
- """
- Processes the given input. Requests starting with a '/' are special
- commands to the interpretor, and anything else is sent to the control port.
- This returns a boolean to indicate if the interpretor should terminate or
- not.
-
- Arguments:
- input - user input to be processed
- """
-
- if not input or not input.strip(): return True
- input = input.strip()
- inputEntry, outputEntry = [(PROMPT, Formats.PROMPT)], []
- conn = torTools.getConn()
-
- # input falls into three general categories:
- # - interpretor command which starts with a '/'
- # - controller commands handled by torTools (this allows for caching,
- # proper handling by the rest of arm, etc)
- # - unrecognized controller command, this has the possability of confusing
- # arm...
-
- if input.startswith("/"):
- # interpretor command
- inputEntry.append((input, Formats.INPUT_INTERPRETOR))
- outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement
-
- # TODO: add /help option
- # TODO: add /write option
- else:
- # controller command
- if " " in input: cmd, arg = input.split(" ", 1)
- else: cmd, arg = input, ""
-
- inputEntry.append((cmd + " ", Formats.INPUT_CMD))
- if arg: inputEntry.append((arg, Formats.INPUT_ARG))
-
- if cmd.upper() == "GETINFO":
- try:
- response = conn.getInfo(arg, suppressExc = False)
- outputEntry.append((response, Formats.OUTPUT))
- except Exception, exc:
- outputEntry.append((str(exc), Formats.ERROR))
- elif cmd.upper() == "SETCONF":
- if "=" in arg:
- param, value = arg.split("=", 1)
-
- try:
- conn.setOption(param.strip(), value.strip())
- except Exception, exc:
- outputEntry.append((str(exc), Formats.ERROR))
- else:
- # TODO: resets the attribute
- outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement
- else:
- try:
- response = conn.getTorCtl().sendAndRecv("%s\r\n" % input)
-
- for entry in response:
- # Response entries are tuples with the response code, body, and
- # extra info. For instance:
- # ('250', 'version=0.2.2.23-alpha (git-b85eb949b528f4d7)', None)
-
- if len(entry) == 3:
- outputEntry.append((entry[1], Formats.OUTPUT))
- except Exception, exc:
- outputEntry.append((str(exc), Formats.ERROR))
-
- self.previousCommands.insert(0, input)
- self.previousCommands = self.previousCommands[:COMMAND_BACKLOG]
-
- promptEntry = self.contents.pop() # removes old prompt entry
- self.contents += _splitOnNewlines(inputEntry)
- self.contents += _splitOnNewlines(outputEntry)
- self.contents.append(promptEntry)
-
- # if too long then crop lines
- cropLines = len(self.contents) - LINES_BACKLOG
- if cropLines > 0: self.contents = self.contents[cropLines:]
-
- return False
-
def handleKey(self, key):
# TODO: allow contents to be searched (with hilighting?)
@@ -193,40 +126,13 @@ class InterpretorPanel(panel.Panel):
if drawLine >= height: break
def _initFormats(self):
- self.formats[Formats.PROMPT] = curses.A_BOLD | uiTools.getColor("green")
- self.formats[Formats.INPUT] = uiTools.getColor("cyan")
- self.formats[Formats.INPUT_INTERPRETOR] = curses.A_BOLD | uiTools.getColor("magenta")
- self.formats[Formats.INPUT_CMD] = curses.A_BOLD | uiTools.getColor("green")
- self.formats[Formats.INPUT_ARG] = curses.A_BOLD | uiTools.getColor("cyan")
- self.formats[Formats.OUTPUT] = uiTools.getColor("blue")
- self.formats[Formats.USAGE] = uiTools.getColor("cyan")
- self.formats[Formats.HELP] = uiTools.getColor("magenta")
- self.formats[Formats.ERROR] = curses.A_BOLD | uiTools.getColor("red")
-
-def _splitOnNewlines(entry):
- """
- Splits a list of (msg, format) tuples on newlines into a list of lines.
-
- Arguments:
- entry - list of display tuples
- """
-
- results, tmpLine = [], []
- entry = list(entry) # shallow copy
-
- while entry:
- msg, format = entry.pop(0)
-
- if "\n" in msg:
- msg, remainder = msg.split("\n", 1)
- entry.insert(0, (remainder, format))
-
- tmpLine.append((msg, format))
- results.append(tmpLine)
- tmpLine = []
- else:
- tmpLine.append((msg, format))
-
- if tmpLine: results.append(tmpLine)
- return results
+ self.formats[torInterpretor.Formats.PROMPT] = curses.A_BOLD | uiTools.getColor("green")
+ self.formats[torInterpretor.Formats.INPUT] = uiTools.getColor("cyan")
+ self.formats[torInterpretor.Formats.INPUT_INTERPRETOR] = curses.A_BOLD | uiTools.getColor("magenta")
+ self.formats[torInterpretor.Formats.INPUT_CMD] = curses.A_BOLD | uiTools.getColor("green")
+ self.formats[torInterpretor.Formats.INPUT_ARG] = curses.A_BOLD | uiTools.getColor("cyan")
+ self.formats[torInterpretor.Formats.OUTPUT] = uiTools.getColor("blue")
+ self.formats[torInterpretor.Formats.USAGE] = uiTools.getColor("cyan")
+ self.formats[torInterpretor.Formats.HELP] = uiTools.getColor("magenta")
+ self.formats[torInterpretor.Formats.ERROR] = curses.A_BOLD | uiTools.getColor("red")
diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py
index 9b6bff4..866495d 100644
--- a/src/util/torInterpretor.py
+++ b/src/util/torInterpretor.py
@@ -6,7 +6,10 @@ directly, history and tab completion.
import readline # simply importing this provides history to raw_input
-from util import enum
+from util import enum, torTools
+
+PROMPT = ">>> "
+Formats = enum.Enum("PROMPT", "INPUT", "INPUT_INTERPRETOR", "INPUT_CMD", "INPUT_ARG", "OUTPUT", "USAGE", "HELP", "ERROR")
TERM_COLORS = ("BLACK", "RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE")
@@ -21,6 +24,13 @@ ATTR_ENCODING = {Attr.BOLD: "1", Attr.UNDERLINE: "4", Attr.HILIGHT: "7"}
CSI = "\x1B[%sm"
RESET = CSI % "0"
+class InterpretorClosed(Exception):
+ """
+ Exception raised when the interpretor should be shut down.
+ """
+
+ pass
+
def format(msg, *attr):
"""
Simple terminal text formatting, using ANSI escape sequences from:
@@ -60,7 +70,125 @@ def prompt():
prompt = format(">>> ", Color.GREEN, Attr.BOLD)
input = ""
+ formatMap = {} # mapping of Format to Color and Attr enums
+ formatMap[Formats.PROMPT] = (Attr.BOLD, Color.GREEN)
+ formatMap[Formats.INPUT] = (Color.CYAN, )
+ formatMap[Formats.INPUT_INTERPRETOR] = (Attr.BOLD, Color.MAGENTA)
+ formatMap[Formats.INPUT_CMD] = (Attr.BOLD, Color.GREEN)
+ formatMap[Formats.INPUT_ARG] = (Attr.BOLD, Color.CYAN)
+ formatMap[Formats.OUTPUT] = (Color.BLUE, )
+ formatMap[Formats.USAGE] = (Color.CYAN, )
+ formatMap[Formats.HELP] = (Color.MAGENTA, )
+ formatMap[Formats.ERROR] = (Attr.BOLD, Color.RED)
+
while input != "/quit":
input = raw_input(prompt)
- print format("echoing back '%s'" % input, Color.BLUE)
+
+ _, outputEntry = handleQuery(input)
+
+ for line in outputEntry:
+ outputLine = ""
+
+ for msg, msgFormat in line:
+ outputLine += format(msg, *formatMap[msgFormat])
+
+ print outputLine
+
+def handleQuery(input):
+ """
+ Processes the given input. Requests starting with a '/' are special
+ commands to the interpretor, and anything else is sent to the control port.
+ This returns an input/output tuple, each entry being a list of lines, each
+ line having a list of (msg, format) tuples for the content to be displayed.
+ This raises a InterpretorClosed if the interpretor should be shut down.
+
+ Arguments:
+ input - user input to be processed
+ """
+
+ input = input.strip()
+ inputEntry, outputEntry = [(PROMPT, Formats.PROMPT)], []
+ conn = torTools.getConn()
+
+ # input falls into three general categories:
+ # - interpretor command which starts with a '/'
+ # - controller commands handled by torTools (this allows for caching,
+ # proper handling by the rest of arm, etc)
+ # - unrecognized controller command, this has the possability of confusing
+ # arm...
+
+ if input.startswith("/"):
+ # interpretor command
+ inputEntry.append((input, Formats.INPUT_INTERPRETOR))
+ outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement
+
+ # TODO: add /help option
+ # TODO: add /write option
+ else:
+ # controller command
+ if " " in input: cmd, arg = input.split(" ", 1)
+ else: cmd, arg = input, ""
+
+ inputEntry.append((cmd + " ", Formats.INPUT_CMD))
+ if arg: inputEntry.append((arg, Formats.INPUT_ARG))
+
+ if cmd.upper() == "GETINFO":
+ try:
+ response = conn.getInfo(arg, suppressExc = False)
+ outputEntry.append((response, Formats.OUTPUT))
+ except Exception, exc:
+ outputEntry.append((str(exc), Formats.ERROR))
+ elif cmd.upper() == "SETCONF":
+ if "=" in arg:
+ param, value = arg.split("=", 1)
+
+ try:
+ conn.setOption(param.strip(), value.strip())
+ except Exception, exc:
+ outputEntry.append((str(exc), Formats.ERROR))
+ else:
+ # TODO: resets the attribute
+ outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement
+ else:
+ try:
+ response = conn.getTorCtl().sendAndRecv("%s\r\n" % input)
+
+ for entry in response:
+ # Response entries are tuples with the response code, body, and
+ # extra info. For instance:
+ # ('250', 'version=0.2.2.23-alpha (git-b85eb949b528f4d7)', None)
+
+ if len(entry) == 3:
+ outputEntry.append((entry[1], Formats.OUTPUT))
+ except Exception, exc:
+ outputEntry.append((str(exc), Formats.ERROR))
+
+ return (_splitOnNewlines(inputEntry), _splitOnNewlines(outputEntry))
+
+def _splitOnNewlines(entry):
+ """
+ Splits a list of (msg, format) tuples on newlines into a list of lines.
+
+ Arguments:
+ entry - list of display tuples
+ """
+
+ results, tmpLine = [], []
+ entry = list(entry) # shallow copy
+
+ while entry:
+ msg, format = entry.pop(0)
+
+ if "\n" in msg:
+ msg, remainder = msg.split("\n", 1)
+ entry.insert(0, (remainder, format))
+
+ tmpLine.append((msg, format))
+ results.append(tmpLine)
+ tmpLine = []
+ else:
+ tmpLine.append((msg, format))
+
+ if tmpLine: results.append(tmpLine)
+ return results