2021-11-04 14:53:50 +00:00
|
|
|
#!/usr/bin/python3
|
2019-03-29 21:07:01 +00:00
|
|
|
import collections
|
2019-03-29 20:01:40 +00:00
|
|
|
import os
|
2019-03-29 15:17:05 +00:00
|
|
|
import struct
|
|
|
|
import sys
|
|
|
|
import time
|
2019-03-29 20:01:40 +00:00
|
|
|
import fcntl
|
|
|
|
import select
|
|
|
|
import termios
|
|
|
|
import tty
|
|
|
|
|
|
|
|
def writeout(data):
|
|
|
|
done = False
|
2021-11-04 14:53:50 +00:00
|
|
|
if isinstance(data, str) and not isinstance(data, bytes):
|
|
|
|
data = data.encode('utf8')
|
2019-04-03 15:44:05 +00:00
|
|
|
try:
|
2021-11-04 14:53:50 +00:00
|
|
|
sys.stdout.buffer.write(data)
|
2019-04-03 15:44:05 +00:00
|
|
|
done = True
|
|
|
|
except IOError:
|
|
|
|
time.sleep(0.1)
|
|
|
|
pass
|
2019-03-29 20:01:40 +00:00
|
|
|
|
2019-04-01 20:56:14 +00:00
|
|
|
|
|
|
|
class LogReplay(object):
|
|
|
|
def __init__(self, logfile, cblfile):
|
2021-11-04 14:53:50 +00:00
|
|
|
self.bin = open(cblfile, 'rb')
|
|
|
|
self.txt = open(logfile, 'rb')
|
2019-04-01 20:56:14 +00:00
|
|
|
self.cleardata = []
|
|
|
|
self.clearidx = 0
|
|
|
|
self.pendingdata = collections.deque([])
|
|
|
|
self.priordata = collections.deque([])
|
|
|
|
self.laststamp = None
|
2019-04-11 20:26:14 +00:00
|
|
|
self.needclear = False
|
2019-04-01 20:56:14 +00:00
|
|
|
|
|
|
|
def _rewind(self, datasize=None):
|
|
|
|
curroffset = self.bin.tell() - 16
|
2019-04-03 14:38:35 +00:00
|
|
|
if self.cleardata and self.clearidx > 1:
|
2019-04-01 20:56:14 +00:00
|
|
|
self.clearidx -= 1
|
2019-04-03 14:38:35 +00:00
|
|
|
priordata = self.cleardata[self.clearidx - 1]
|
2019-04-01 20:56:14 +00:00
|
|
|
return curroffset, priordata
|
2019-04-03 14:38:35 +00:00
|
|
|
self.cleardata = []
|
|
|
|
self.clearidx = 0
|
2019-04-01 20:56:14 +00:00
|
|
|
newoffset = curroffset - 32
|
|
|
|
if newoffset < 0: #TODO: Follow a log roll
|
|
|
|
newoffset = 0
|
|
|
|
if datasize:
|
|
|
|
while datasize > 0 and newoffset > 0:
|
|
|
|
self.bin.seek(newoffset)
|
|
|
|
tmprec = self.bin.read(16)
|
|
|
|
newoffset -= 32
|
|
|
|
tmprec = struct.unpack('!BBIHIBBH', tmprec)
|
|
|
|
if tmprec[1] == 2:
|
|
|
|
datasize -= tmprec[3]
|
|
|
|
if newoffset >= 0:
|
|
|
|
self.bin.seek(newoffset)
|
|
|
|
return curroffset, None
|
|
|
|
|
|
|
|
def debuginfo(self):
|
|
|
|
return '{0}, {1}'.format(self.bin.tell(), self.clearidx)
|
|
|
|
|
|
|
|
def get_output(self, reverse=False):
|
|
|
|
endoffset = None
|
2021-11-04 14:53:50 +00:00
|
|
|
output = b''
|
2019-04-01 20:56:14 +00:00
|
|
|
if reverse: # Forget the uncommited future, if present
|
2021-11-04 14:53:50 +00:00
|
|
|
output += b'\x1b[2J\x1b[H'
|
2019-04-01 20:56:14 +00:00
|
|
|
endoffset, priordata = self._rewind(4096)
|
|
|
|
if priordata is not None:
|
|
|
|
return priordata, 1
|
2019-04-11 20:26:14 +00:00
|
|
|
elif self.needclear:
|
2021-11-04 14:53:50 +00:00
|
|
|
output += b'\x1b[2J\x1b[H'
|
2019-04-11 20:26:14 +00:00
|
|
|
self.needclear = False
|
2019-04-01 20:56:14 +00:00
|
|
|
if self.cleardata and self.clearidx < len(self.cleardata):
|
|
|
|
datachunk = self.cleardata[self.clearidx]
|
|
|
|
self.clearidx += 1
|
|
|
|
return datachunk, 1
|
|
|
|
self.cleardata = []
|
|
|
|
self.clearidx = 0
|
|
|
|
while (not reverse) or (self.bin.tell() < endoffset):
|
|
|
|
record = self.bin.read(16)
|
2019-04-11 20:26:14 +00:00
|
|
|
if not record:
|
2021-11-04 14:53:50 +00:00
|
|
|
return b'', 0
|
2019-04-01 20:56:14 +00:00
|
|
|
record = struct.unpack('!BBIHIBBH', record)
|
|
|
|
if record[0] > 16:
|
|
|
|
# Unsupported record, skip
|
|
|
|
self.bin.seek(record[0] - 16, 1)
|
|
|
|
continue
|
|
|
|
type = record[1]
|
|
|
|
offset = record[2]
|
|
|
|
size = record[3]
|
|
|
|
evtdata = record[5]
|
|
|
|
auxdata = record[6]
|
|
|
|
if type == 3:
|
|
|
|
#TODO: provide data for status bar
|
|
|
|
continue
|
|
|
|
elif type == 2:
|
|
|
|
self.laststamp = record[4]
|
|
|
|
self.txt.seek(offset)
|
|
|
|
txtout = self.txt.read(size)
|
|
|
|
if reverse and self.bin.tell() < endoffset:
|
|
|
|
output += txtout
|
|
|
|
continue
|
2021-11-04 14:53:50 +00:00
|
|
|
if b'\x1b[2J' in txtout:
|
|
|
|
self.cleardata = txtout.split(b'\x1b[2J')
|
2019-04-03 14:38:35 +00:00
|
|
|
for idx in range(1, len(self.cleardata)):
|
2021-11-04 14:53:50 +00:00
|
|
|
self.cleardata[idx] = b'\x1b[2J' + self.cleardata[idx]
|
2019-04-01 20:56:14 +00:00
|
|
|
self.clearidx = 0
|
2019-04-03 14:38:35 +00:00
|
|
|
if not self.cleardata[0]:
|
|
|
|
self.cleardata = self.cleardata[1:]
|
2019-04-01 20:56:14 +00:00
|
|
|
if self.cleardata:
|
2019-04-03 14:38:35 +00:00
|
|
|
if reverse:
|
|
|
|
output = self.cleardata[-1]
|
|
|
|
self.clearidx = len(self.cleardata)
|
|
|
|
else:
|
|
|
|
output += self.cleardata[0]
|
|
|
|
self.clearidx = 1
|
2019-04-01 20:56:14 +00:00
|
|
|
else:
|
|
|
|
output += txtout
|
|
|
|
break
|
|
|
|
if endoffset is not None and endoffset >= 0:
|
|
|
|
self.bin.seek(endoffset)
|
|
|
|
return output, 1
|
|
|
|
|
2019-04-11 20:26:14 +00:00
|
|
|
def begin(self):
|
|
|
|
self.needclear = True
|
|
|
|
self.bin.seek(0)
|
|
|
|
|
|
|
|
def end(self):
|
|
|
|
self.bin.seek(0, 2)
|
|
|
|
|
2019-04-01 20:56:14 +00:00
|
|
|
|
|
|
|
def main(txtfile, binfile):
|
|
|
|
replay = LogReplay(txtfile, binfile)
|
2019-03-29 20:01:40 +00:00
|
|
|
oldtcattr = termios.tcgetattr(sys.stdin.fileno())
|
|
|
|
tty.setraw(sys.stdin.fileno())
|
|
|
|
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
|
|
|
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl | os.O_NONBLOCK)
|
2019-04-01 20:56:14 +00:00
|
|
|
reverse = False
|
|
|
|
skipnext = False
|
2019-04-03 15:44:05 +00:00
|
|
|
quitit = False
|
2019-04-03 14:38:35 +00:00
|
|
|
writeout('\x1b[2J\x1b[;H')
|
2019-04-01 20:56:14 +00:00
|
|
|
try:
|
2019-04-03 15:44:05 +00:00
|
|
|
while not quitit:
|
2019-04-01 20:56:14 +00:00
|
|
|
if not skipnext:
|
|
|
|
newdata, delay = replay.get_output(reverse)
|
|
|
|
skipnext = False
|
|
|
|
reverse = False
|
2019-04-11 20:26:14 +00:00
|
|
|
if newdata:
|
|
|
|
writeout(newdata)
|
|
|
|
writeout('\x1b]0;[Time: {0}]\x07'.format(
|
|
|
|
time.strftime('%m/%d %H:%M:%S', time.localtime(replay.laststamp))))
|
2021-11-04 14:53:50 +00:00
|
|
|
try:
|
|
|
|
sys.stdout.buffer.flush()
|
|
|
|
except IOError:
|
|
|
|
pass
|
2019-04-03 15:44:05 +00:00
|
|
|
while True:
|
|
|
|
select.select((sys.stdin,), (), (), 86400)
|
|
|
|
myinput = sys.stdin.read()
|
2019-04-11 20:26:14 +00:00
|
|
|
if myinput.startswith('\x1b[C') or myinput.startswith('\x1bOC') or myinput == '\r': # right
|
2019-04-03 15:44:05 +00:00
|
|
|
break
|
2019-04-11 20:26:14 +00:00
|
|
|
elif myinput.startswith('\x1b[D') or myinput.startswith('\x1bOD') or myinput == 'y': # left
|
2019-04-03 15:44:05 +00:00
|
|
|
writeout('\x1b[2J\x1b[;H')
|
|
|
|
reverse = True
|
|
|
|
break
|
2019-04-11 20:26:14 +00:00
|
|
|
elif myinput == 'G' or myinput.startswith('\x1b[F'):
|
|
|
|
replay.end()
|
|
|
|
reverse = True
|
|
|
|
break
|
|
|
|
elif myinput == 'g' or myinput.startswith('\x1b[H'):
|
|
|
|
replay.begin()
|
|
|
|
break
|
|
|
|
elif myinput.lower() == 'q' or myinput == '\x03':
|
2019-04-03 15:44:05 +00:00
|
|
|
quitit = True
|
|
|
|
break
|
|
|
|
elif myinput.lower() == 'd':
|
|
|
|
writeout('\x1b];{0}\x07'.format(replay.debuginfo()))
|
|
|
|
sys.stdout.flush()
|
2019-04-11 20:26:14 +00:00
|
|
|
else:
|
|
|
|
pass # print(repr(myinput))
|
2019-04-01 20:56:14 +00:00
|
|
|
except Exception:
|
|
|
|
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
|
|
|
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
|
|
|
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
|
|
|
|
writeout('\x1b[m')
|
|
|
|
raise
|
2019-03-29 20:01:40 +00:00
|
|
|
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
|
|
|
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
|
|
|
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
|
2019-04-01 20:56:14 +00:00
|
|
|
writeout('\x1b[m')
|
2019-03-29 15:17:05 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2019-04-01 20:56:14 +00:00
|
|
|
txtfile = sys.argv[1]
|
2019-04-11 20:26:14 +00:00
|
|
|
if len(sys.argv) > 2:
|
|
|
|
binfile = sys.argv[2]
|
|
|
|
else:
|
|
|
|
if os.path.exists(txtfile + '.cbl'):
|
|
|
|
binfile = txtfile + '.cbl'
|
|
|
|
else:
|
|
|
|
fileparts = txtfile.split('.')
|
|
|
|
prefix = '.'.join(fileparts[:-1])
|
|
|
|
binfile = prefix + '.cbl.' + fileparts[-1]
|
|
|
|
if not os.path.exists(binfile):
|
|
|
|
sys.stderr.write('Unable to locate cbl file\n')
|
|
|
|
sys.exit(1)
|
2019-04-01 20:56:14 +00:00
|
|
|
main(txtfile, binfile)
|