[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [arm/release] Tab completion for control prompt input
commit aaec744638d997447db3b4c50a3080d93835dd12
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Aug 28 17:41:17 2011 -0700
Tab completion for control prompt input
This provides autocompletion for the control prompt based on the capabilities
of the attached tor instance (fetching options from GETINFO info/names,
config/names, etc). This uses readline so it's working for the terminal prompt
but not the panel interpretor (which will need a validator implementation).
---
src/util/torInterpretor.py | 122 +++++++++++++++++++++++++++++++++++++++++--
1 files changed, 116 insertions(+), 6 deletions(-)
diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py
index 866495d..fecc2ac 100644
--- a/src/util/torInterpretor.py
+++ b/src/util/torInterpretor.py
@@ -62,14 +62,115 @@ def format(msg, *attr):
if encodings:
return (CSI % ";".join(encodings)) + msg + RESET
- else:
- raise IOError("BLARG! %s" % str(attr))
- return msg
+ else: return msg
+
+class TorCommandOptions:
+ """
+ Command autocompleter, fetching the valid options from the attached Tor
+ instance.
+ """
+
+ def __init__(self):
+ self.commands = []
+ conn = torTools.getConn()
+
+ # adds all of the valid GETINFO options
+ infoOptions = conn.getInfo("info/names")
+ if infoOptions:
+ for line in infoOptions.split("\n"):
+ if " " in line:
+ # skipping non-existant options mentioned in:
+ # https://trac.torproject.org/projects/tor/ticket/3844
+
+ if line.startswith("config/*") or line.startswith("dir-usage"):
+ continue
+
+ # strips off the ending asterisk if it accepts a value
+ infoOpt = line.split(" ", 1)[0]
+
+ if infoOpt.endswith("*"):
+ infoOpt = infoOpt[:-1]
+
+ self.commands.append("GETINFO %s" % infoOpt)
+ else: self.commands.append("GETINFO ")
+
+ # adds all of the valid GETCONF / SETCONF / RESETCONF options
+ confOptions = conn.getInfo("config/names")
+ if confOptions:
+ # individual options are '<name> <type>' pairs
+ confEntries = [opt.split(" ", 1)[0] for opt in confOptions.split("\n")]
+ self.commands += ["GETCONF %s" % conf for conf in confEntries]
+ self.commands += ["SETCONF %s " % conf for conf in confEntries]
+ self.commands += ["RESETCONF %s" % conf for conf in confEntries]
+ else:
+ self.commands.append("GETCONF ")
+ self.commands.append("SETCONF ")
+ self.commands.append("RESETCONF ")
+
+ # adds all of the valid SETEVENT options
+ eventOptions = conn.getInfo("events/names")
+ if eventOptions:
+ self.commands += ["SETEVENT %s" % event for event in eventOptions.split(" ")]
+ else: self.commands.append("SETEVENT ")
+
+ # adds all of the valid USEFEATURE options
+ featureOptions = conn.getInfo("features/names")
+ if featureOptions:
+ self.commands += ["USEFEATURE %s" % feature for feature in featureOptions.split(" ")]
+ else: self.commands.append("USEFEATURE ")
+
+ # adds all of the valid SIGNAL options
+ # this can't yet be fetched dynamically, as per:
+ # https://trac.torproject.org/projects/tor/ticket/3842
+
+ signals = ("RELOAD", "SHUTDOWN", "DUMP", "DEBUG", "HALT", "HUP", "INT",
+ "USR1", "USR2", "TERM", "NEWNYM", "CLEARDNSCACHE")
+ self.commands += ["SIGNAL %s" % sig for sig in signals]
+
+ # shouldn't use AUTHENTICATE since we only provide the prompt with an
+ # authenticated controller connection
+ #self.commands.append("AUTHENTICATE")
+
+ # other options
+ self.commands.append("SAVECONF")
+ self.commands.append("MAPADDRESS ")
+ self.commands.append("EXTENDCIRCUIT ")
+ self.commands.append("SETCIRCUITPURPOSE ")
+ self.commands.append("SETROUTERPURPOSE ")
+ self.commands.append("ATTACHSTREAM ")
+ self.commands.append("+POSTDESCRIPTOR ") # TODO: needs to support multiline options for this (ugg)
+ self.commands.append("REDIRECTSTREAM ")
+ self.commands.append("CLOSESTREAM ")
+ self.commands.append("CLOSECIRCUIT ")
+ self.commands.append("RESOLVE ")
+ self.commands.append("PROTOCOLINFO ")
+ self.commands.append("+LOADCONF") # TODO: another multiline...
+ self.commands.append("TAKEOWNERSHIP")
+ self.commands.append("QUIT") # TODO: give a confirmation when the user does this?
+
+ def complete(self, text, state):
+ # provides case insensetive autocompletion options based on self.commands
+ for cmd in self.commands:
+ if cmd.lower().startswith(text.lower()):
+ if not state: return cmd
+ else: state -= 1
def prompt():
prompt = format(">>> ", Color.GREEN, Attr.BOLD)
input = ""
+ # sets up tab autocompetion
+ torCommands = TorCommandOptions()
+ readline.parse_and_bind("tab: complete")
+ readline.set_completer(torCommands.complete)
+
+ # Essentially disables autocompletion by word delimiters. This is because
+ # autocompletion options are full commands (ex. "GETINFO version") so we want
+ # "GETINFO" to match to all the options rather than be treated as a complete
+ # command by itself.
+
+ readline.set_completer_delims("\n")
+
formatMap = {} # mapping of Format to Color and Attr enums
formatMap[Formats.PROMPT] = (Attr.BOLD, Color.GREEN)
formatMap[Formats.INPUT] = (Color.CYAN, )
@@ -82,7 +183,13 @@ def prompt():
formatMap[Formats.ERROR] = (Attr.BOLD, Color.RED)
while input != "/quit":
- input = raw_input(prompt)
+ try:
+ input = raw_input(prompt)
+ except:
+ # moves cursor to the next line and terminates (most commonly
+ # KeyboardInterrupt and EOFErro)
+ print
+ break
_, outputEntry = handleQuery(input)
@@ -129,16 +236,19 @@ def handleQuery(input):
if " " in input: cmd, arg = input.split(" ", 1)
else: cmd, arg = input, ""
+ # makes commands uppercase to match the spec
+ cmd = cmd.upper()
+
inputEntry.append((cmd + " ", Formats.INPUT_CMD))
if arg: inputEntry.append((arg, Formats.INPUT_ARG))
- if cmd.upper() == "GETINFO":
+ if cmd == "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":
+ elif cmd == "SETCONF":
if "=" in arg:
param, value = arg.split("=", 1)
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits