Use prompt_toolkit instead of readline

This commit is contained in:
Asger Hautop Drewsen
2015-06-03 14:24:35 +02:00
parent e6479978d4
commit a0aa783443
2 changed files with 70 additions and 77 deletions
+17 -8
View File
@@ -15,10 +15,14 @@ import frida
def await_enter():
if sys.version_info[0] >= 3:
input()
else:
raw_input()
try:
if sys.version_info[0] >= 3:
input()
else:
raw_input()
except (KeyboardInterrupt, EOFError):
print('')
pass
class ConsoleApplication(object):
def __init__(self, run_until_return=await_enter):
@@ -101,6 +105,8 @@ class ConsoleApplication(object):
if self._device is not None:
self._device.off('lost', self._schedule_on_device_lost)
mgr.off('changed', on_devices_changed)
# FIXME:
os.system('kill {}'.format(os.getpid()))
frida.shutdown()
sys.exit(self._exit_status)
@@ -128,6 +134,8 @@ class ConsoleApplication(object):
def _exit(self, exit_status):
self._exit_status = exit_status
# FIXME:
os.system('kill {}'.format(os.getpid()))
self._reactor.stop()
def _try_start(self):
@@ -231,13 +239,14 @@ class Reactor(object):
with self._lock:
self._running = True
def termination_watcher():
self._run_until_return()
self.stop()
watcher_thread = threading.Thread(target=termination_watcher)
watcher_thread = threading.Thread(target=self._run)
watcher_thread.daemon = True
watcher_thread.start()
self._run_until_return()
self.stop()
def _run(self):
running = True
while running:
now = time.time()
+53 -69
View File
@@ -1,53 +1,29 @@
from __future__ import unicode_literals
def main():
import frida
from frida.application import ConsoleApplication
from colorama import Fore, Style
import json
import platform
HAVE_READLINE = True
if platform.system() == "Darwin":
# We really want to avoid libedit
try:
import gnureadline as readline
except Exception as e:
HAVE_READLINE = False
print(Fore.RED + Style.BRIGHT + """
WARNING: Unable to find package 'gnureadline' needed for tab completion;
please brace yourself for a massively degraded user experience!
""" + Style.RESET_ALL)
else:
try:
import readline
except Exception as e:
HAVE_READLINE = False
import sys
import threading
import os
if HAVE_READLINE:
HIST_FILE = os.path.join(os.path.expanduser("~"), ".frida_history")
from prompt_toolkit.shortcuts import get_input
from prompt_toolkit.history import FileHistory
from prompt_toolkit.completion import Completion, Completer
from pygments.lexers import JavascriptLexer
from pygments.token import Token
class REPLApplication(ConsoleApplication):
def __init__(self):
if HAVE_READLINE:
readline.parse_and_bind("tab: complete")
# force completor to run on first tab press
readline.parse_and_bind("set show-all-if-unmodified on")
readline.parse_and_bind("set show-all-if-ambiguous on")
# Set our custom completer
readline.set_completer(self._completer)
try:
readline.read_history_file(HIST_FILE)
except IOError:
pass
self._script = None
self._seqno = 0
self._ready = threading.Event()
self._response_cond = threading.Condition()
self._response_data = None
self._completor_locals = []
self._history = FileHistory(os.path.join(os.path.expanduser('~'), '.frida_history'))
self._completer = FridaCompleter(self)
super(REPLApplication, self).__init__(self._process_input)
@@ -101,11 +77,6 @@ WARNING: Unable to find package 'gnureadline' needed for tab completion;
self._script = None
def _process_input(self):
if sys.version_info[0] >= 3:
input_impl = input
else:
input_impl = raw_input
self._print_startup_message()
self._ready.wait()
@@ -114,25 +85,20 @@ WARNING: Unable to find package 'gnureadline' needed for tab completion;
line = ""
while len(expression) == 0 or line.endswith("\\"):
try:
if len(expression) == 0:
line = input_impl("[%s]" % self._prompt_string + "-> ")
else:
line = input_impl("... ")
prompt = "[%s]" % self._prompt_string + "-> " if len(expression) == 0 else "... "
line = get_input(prompt, history=self._history, lexer=JavascriptLexer, completer=self._completer)
except EOFError:
# An extra newline after EOF to exit the REPL cleanly
print("\nThank you for using Frida!")
return
except KeyboardInterrupt:
line = ""
continue
if len(line.strip()) > 0:
if len(expression) > 0:
expression += "\n"
expression += line.rstrip("\\")
if HAVE_READLINE:
try:
readline.write_history_file(HIST_FILE)
except IOError:
pass
if expression.endswith("?"):
try:
self._print_help(expression)
@@ -265,28 +231,6 @@ WARNING: Unable to find package 'gnureadline' needed for tab completion;
return prompt_string
def _completer(self, prefix, index):
if index == 0:
if not prefix:
self._completor_locals = [key for key in self._evaluate("Object.keys(this)")[1] if not key.startswith('_')]
else:
if prefix.endswith("."):
thing_to_check = prefix.split(' ')[0][:-1]
self._completor_locals = self._evaluate("Object.keys(" + thing_to_check + ")")[1]
else:
if "." in prefix:
target = prefix.split(' ')[-1]
needle = target.split('.')[-1]
target = ".".join(target.split('.')[:-1])
self._completor_locals = [target + "." + key for key in self._evaluate("Object.keys(" + target + ")")[1] if key.startswith(needle)]
else:
self._completor_locals = [key for key in self._evaluate("Object.keys(this)")[1] if key.startswith(prefix)]
try:
return self._completor_locals[index]
except IndexError:
return None
def _evaluate(self, text):
self._reactor.schedule(lambda: self._script.post_message({'name': '.evaluate', 'payload': {'expression': text}}))
with self._response_cond:
@@ -382,6 +326,46 @@ WARNING: Unable to find package 'gnureadline' needed for tab completion;
}).call(this);
"""
class FridaCompleter(Completer):
def __init__(self, repl):
self._repl = repl
self._lexer = JavascriptLexer()
def get_completions(self, document, complete_event):
prefix = document.text_before_cursor
tokens = list(self._lexer.get_tokens(prefix))[:-1]
word_tokens = 0
before_dot = ''
after_dot = ''
encountered_dot = False
for t in tokens[::-1]:
if t[0] == Token.Name.Other:
before_dot = t[1] + before_dot
word_tokens += 1
elif t[0] == Token.Punctuation and t[1] == '.':
before_dot = '.' + before_dot
if not encountered_dot:
encountered_dot = True
after_dot = before_dot[1:]
before_dot = ''
word_tokens += 1
else:
break
try:
if encountered_dot:
for key in self._repl._evaluate("Object.keys(" + before_dot + ")")[1]:
if key.startswith(after_dot):
yield Completion(key, -len(after_dot))
else:
for key in self._repl._evaluate("Object.keys(this)")[1]:
if not key.startswith(before_dot) or (key.startswith('_') and before_dot == ''):
continue
yield Completion(key, -len(before_dot))
except Exception:
pass
def hexdump(src, length=16):
try:
xrange