2014-01-11 22:47:51 +00:00
|
|
|
import os
|
2014-01-10 02:55:21 +00:00
|
|
|
import re
|
2014-01-07 03:41:03 +00:00
|
|
|
import select
|
|
|
|
import socket
|
2014-01-10 02:55:21 +00:00
|
|
|
import sys
|
|
|
|
|
2014-04-13 20:44:37 +00:00
|
|
|
try:
|
|
|
|
from StringIO import StringIO
|
|
|
|
except ImportError:
|
|
|
|
from io import StringIO
|
2014-01-07 03:41:03 +00:00
|
|
|
|
2014-01-07 05:16:36 +00:00
|
|
|
def noop():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def vim_encode(data):
|
|
|
|
if isinstance(data, list):
|
|
|
|
return "[" + ",".join([vim_encode(x) for x in data]) + "]"
|
|
|
|
elif isinstance(data, dict):
|
|
|
|
return "{" + ",".join([vim_encode(x)+":"+vim_encode(y) for x,y in data.items()]) + "}"
|
|
|
|
elif isinstance(data, str):
|
|
|
|
str_list = []
|
|
|
|
for c in data:
|
2014-01-29 07:20:07 +00:00
|
|
|
if (0 <= ord(c) and ord(c) <= 31) or c == '"' or c == "\\":
|
2014-01-15 18:28:12 +00:00
|
|
|
str_list.append("\\%03o" % ord(c))
|
2014-01-07 05:16:36 +00:00
|
|
|
else:
|
|
|
|
str_list.append(c)
|
|
|
|
return '"' + ''.join(str_list) + '"'
|
|
|
|
elif isinstance(data, int):
|
|
|
|
return str(data)
|
|
|
|
else:
|
|
|
|
raise TypeError("can't encode a " + type(data).__name__)
|
|
|
|
|
|
|
|
def bdecode(f, char=None):
|
2014-01-09 00:48:12 +00:00
|
|
|
if char == None:
|
|
|
|
char = f.read(1)
|
|
|
|
if char == 'l':
|
|
|
|
l = []
|
|
|
|
while True:
|
2014-01-07 05:16:36 +00:00
|
|
|
char = f.read(1)
|
2014-01-09 00:48:12 +00:00
|
|
|
if char == 'e':
|
|
|
|
return l
|
|
|
|
l.append(bdecode(f, char))
|
|
|
|
elif char == 'd':
|
|
|
|
d = {}
|
|
|
|
while True:
|
|
|
|
char = f.read(1)
|
|
|
|
if char == 'e':
|
|
|
|
return d
|
|
|
|
key = bdecode(f, char)
|
|
|
|
d[key] = bdecode(f)
|
|
|
|
elif char == 'i':
|
2014-06-18 02:35:21 +00:00
|
|
|
i = ''
|
2014-01-09 00:48:12 +00:00
|
|
|
while True:
|
|
|
|
char = f.read(1)
|
|
|
|
if char == 'e':
|
2014-06-18 02:35:21 +00:00
|
|
|
return int(i)
|
|
|
|
i += char
|
2014-01-09 00:48:12 +00:00
|
|
|
elif char.isdigit():
|
|
|
|
i = int(char)
|
|
|
|
while True:
|
|
|
|
char = f.read(1)
|
|
|
|
if char == ':':
|
|
|
|
return f.read(i)
|
|
|
|
i = 10 * i + int(char)
|
|
|
|
elif char == '':
|
|
|
|
raise EOFError("unexpected end of bencode data")
|
|
|
|
else:
|
|
|
|
raise TypeError("unexpected type "+char+"in bencode data")
|
2014-01-07 05:16:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Connection:
|
2014-01-08 04:07:31 +00:00
|
|
|
def __init__(self, host, port, custom_poll=noop, keepalive_file=None):
|
|
|
|
self.custom_poll = custom_poll
|
|
|
|
self.keepalive_file = keepalive_file
|
2014-01-07 05:16:36 +00:00
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
s.settimeout(8)
|
|
|
|
s.connect((host, int(port)))
|
2014-01-07 03:41:03 +00:00
|
|
|
s.setblocking(1)
|
2014-01-07 05:16:36 +00:00
|
|
|
self.socket = s
|
|
|
|
|
2014-01-08 04:07:31 +00:00
|
|
|
def poll(self):
|
|
|
|
self.custom_poll()
|
|
|
|
if self.keepalive_file and not os.path.exists(self.keepalive_file):
|
|
|
|
exit(0)
|
|
|
|
|
2014-01-07 05:16:36 +00:00
|
|
|
def close(self):
|
|
|
|
return self.socket.close()
|
|
|
|
|
|
|
|
def send(self, payload):
|
2014-04-13 20:44:37 +00:00
|
|
|
if sys.version_info[0] >= 3:
|
|
|
|
self.socket.sendall(bytes(payload, 'UTF-8'))
|
|
|
|
else:
|
|
|
|
self.socket.sendall(payload)
|
2014-01-07 05:16:36 +00:00
|
|
|
return ''
|
|
|
|
|
|
|
|
def receive(self, char=None):
|
|
|
|
f = self.socket.makefile()
|
2014-04-13 20:44:37 +00:00
|
|
|
while len(select.select([f], [], [], 0.1)[0]) == 0:
|
|
|
|
self.poll()
|
2014-01-07 05:16:36 +00:00
|
|
|
try:
|
|
|
|
return bdecode(f)
|
|
|
|
finally:
|
|
|
|
f.close()
|
|
|
|
|
2014-01-10 03:41:51 +00:00
|
|
|
def call(self, payload, terminators, selectors):
|
2014-01-07 05:16:36 +00:00
|
|
|
self.send(payload)
|
|
|
|
responses = []
|
2014-01-07 03:41:03 +00:00
|
|
|
while True:
|
2014-01-10 03:41:51 +00:00
|
|
|
response = self.receive()
|
|
|
|
for key in selectors:
|
|
|
|
if response[key] != selectors[key]:
|
|
|
|
continue
|
|
|
|
responses.append(response)
|
|
|
|
if 'status' in response and set(terminators) & set(response['status']):
|
2014-01-07 05:16:36 +00:00
|
|
|
return responses
|
2014-01-07 03:41:03 +00:00
|
|
|
|
2014-01-08 04:53:20 +00:00
|
|
|
def dispatch(host, port, poll, keepalive, command, *args):
|
2014-01-08 04:07:31 +00:00
|
|
|
conn = Connection(host, port, poll, keepalive)
|
2014-01-07 05:16:36 +00:00
|
|
|
try:
|
|
|
|
return getattr(conn, command)(*args)
|
|
|
|
finally:
|
|
|
|
conn.close()
|
2014-01-07 03:41:03 +00:00
|
|
|
|
2014-01-08 04:53:20 +00:00
|
|
|
def main(host, port, keepalive, command, *args):
|
2014-01-07 03:41:03 +00:00
|
|
|
try:
|
2014-01-10 02:55:21 +00:00
|
|
|
sys.stdout.write(vim_encode(dispatch(host, port, noop, keepalive, command, *[bdecode(StringIO(arg)) for arg in args])))
|
2014-04-13 20:44:37 +00:00
|
|
|
except Exception:
|
|
|
|
print((sys.exc_info()[1]))
|
2014-01-07 03:41:03 +00:00
|
|
|
exit(1)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main(*sys.argv[1:])
|