Skip to content

Commit

Permalink
Adds width attribute which specifies number of bytes printed per line
Browse files Browse the repository at this point in the history
  • Loading branch information
Utkarsh1308 committed Jun 1, 2019
1 parent ff94ed2 commit 5afcf55
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 72 deletions.
101 changes: 43 additions & 58 deletions multidiff/Render.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from multidiff.Ansi import Ansi
import binascii
import html
import textwrap
import os

class Render():
def __init__(self, encoder='hexdump', color='ansi'):
def __init__(self, encoder='hexdump', color='html', width='82'):
'''Configure the output encoding and coloring method of this rendering object'''
if color == 'ansi':
self.highligther = ansi_colored
Expand All @@ -16,17 +18,21 @@ def __init__(self, encoder='hexdump', color='ansi'):
self.encoder = HexEncoder
elif encoder == 'utf8':
self.encoder = Utf8Encoder


self.width = width

def render(self, model, diff):
'''Render the diff in the given model into a UTF-8 String'''
result = self.encoder(self.highligther)
obj = model.objects[diff.target]
obj1 = model.objects[diff.target-1]
obj2 = model.objects[diff.target]
for op in diff.opcodes:
data = obj.data[op[3]:op[4]]
if type(data) == bytes:
result.append(data, op[0])
elif type(data) == str:
result.append(bytes(data, "utf8"), op[0])
data1 = obj1.data[op[1]:op[2]]
data2 = obj2.data[op[3]:op[4]]
if type(data2) == bytes:
result.append(data1, data2, op[0], self.width)
elif type(data2) == str:
result.append(bytes(data1, "utf8"), bytes(data2, "utf8"), op[0], self.width)
return result.final()

def dumps(self, model):
Expand All @@ -44,7 +50,7 @@ def __init__(self, highligther):

def append(self, data, color):
self.output += self.highligther(str(data, 'utf8'), color)

def final(self):
return self.output

Expand All @@ -56,7 +62,7 @@ def __init__(self, highligther):
def append(self, data, color):
data = str(binascii.hexlify(data),'utf8')
self.output += self.highligther(data, color)

def final(self):
return self.output

Expand All @@ -69,78 +75,57 @@ def __init__(self, highligther):
self.rowlen = 0
self.hexrow = ''
self.skipspace = False
self.asciirow = ''

def append(self, data, color):
if len(data) == 0:
self._append(data, color)
while len(data) > 0:
def append(self, data1, data2, color, width):
if len(data2) == 0:
self._append(data1, data2, color, width)
while len(data2) > 0:
if self.rowlen == 16:
self._newrow()
consumed = self._append(data[:16 - self.rowlen], color)
data = data[consumed:]

def _append(self, data, color):
if len(data) == 0:
#in the case of highlightig a deletion in a target or an
#addition in the source, print a highlighted space and mark
#it skippanble for the next append
hexs = ' '
self.skipspace = True
consumed = self._append(data1[:16 - self.rowlen], data2[:16 - self.rowlen], color, width)
data2 = data2[consumed:]

def _append(self, data1, data2, color, width):
# <deletion>
if len(data2) == 0:
hexs = str(binascii.hexlify(data1), 'utf8')
hexs = ' '.join([hexs[i:i+2] for i in range(0, len(hexs), 2)])
else:
self._add_hex_space()
#encode to hex and add some spaces
hexs = str(binascii.hexlify(data), 'utf8')
hexs = str(binascii.hexlify(data2), 'utf8')
hexs = ' '.join([hexs[i:i+2] for i in range(0, len(hexs), 2)])
asciis = ''
#make the ascii dump
for byte in data:
if 0x20 <= byte <= 0x7E:
asciis += chr(byte)
else:
asciis += '.'
self.asciirow += self.highligther(asciis, color)

self.hexrow += self.highligther(hexs, color)
self.rowlen += len(data)
return len(data)

self.hexrow += self.highligther(hexs, color)
if len(self.hexrow) > int(width):
self.hexrow = textwrap.fill(self.hexrow, int(width))
self.rowlen += len(data2)
return len(data2)

def _newrow(self):
self._add_hex_space()
# self._add_hex_space()
if self.addr != 0:
self.body += '\n'
self.body += "{:06x}:{:s}|{:s}|".format(
self.addr, self.hexrow, self.asciirow);
self.body = self.body
ops = ['insert', 'delete', 'replace']
if any(ext in self.hexrow for ext in ops):
self.body += "\n{:06x}:{:s}".format(
self.addr, self.hexrow);
self.addr += 16
self.rowlen = 0
self.hexrow = ''
self.asciirow = ''

def _add_hex_space(self):
if self.skipspace:
self.skipspace = False
else:
self.hexrow += ' '


def final(self):
self.hexrow += 3*(16 - self.rowlen) * ' '
self.asciirow += (16 - self.rowlen) * ' '
# self.hexrow += 3*(16 - self.rowlen) * ' '
self._newrow()
return self.body

def ansi_colored(string, op):
if op == 'equal':
return string
elif op == 'replace':
color = Ansi.replace
elif op == 'insert':
color = Ansi.insert
elif op == 'delete':
color = Ansi.delete
return color + string + Ansi.reset

def html_colored(string, op):
if op == 'equal':
return string
return "<span class='" + op + "'>" + html.escape(string) + "</span>"
return "<" + op + ">" + html.escape(string) + "</" + op + ">"
6 changes: 3 additions & 3 deletions multidiff/StreamView.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
class StreamView():
'''A class for building UIs. Has some pretty serious side effects.
Use Render instead if you're not making a long-running UI'''
def __init__(self, model, encoding='hexdump', mode='sequence', color='ansi'):
def __init__(self, model, encoding='hexdump', mode='sequence', color='html', width='82'):
self.color = color
self.render = Render(color=color, encoder=encoding)
self.render = Render(color=color, encoder=encoding, width=width)
self.mode = mode
self.model = model
model.add_listener(self)
Expand All @@ -20,7 +20,7 @@ def diff_added(self, diff):
if self.color == 'ansi':
print(Ansi.bold + self.model.objects[diff.target].info + Ansi.reset)
elif self.color == 'html':
print('<span style="font-weight: bold;">' + html.escape(self.model.objects[diff.target].info) + '</span>')
print(html.escape(self.model.objects[diff.target-1].info) + ' --> ' + html.escape(self.model.objects[diff.target].info))
print(self.render.render(self.model, diff))
#StreamView is designed to run for long times so we delete
#old objects and diffs to not leak memory
Expand Down
36 changes: 25 additions & 11 deletions multidiff/command_line_interface.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
#!/usr/bin/python3
import argparse
from multidiff import MultidiffModel, StreamView, SocketController, FileController, StdinController
import os

def main():

rows, columns = os.popen('stty size', 'r').read().split()
columns = int(columns)

args = make_parser().parse_args()
m = MultidiffModel()
v = StreamView(m, encoding=args.outformat, mode=args.mode, color=args.color)


if args.width == 'max':
args.width = columns

v = StreamView(m, encoding=args.outformat, mode=args.mode, color=args.color, width=args.width)

if len(args.file) > 0:
informat = args.informat if args.informat else 'raw'
files = FileController(m, informat)
Expand All @@ -32,17 +41,17 @@ def make_parser():
│ sensor module │
└───────────────┘
""")

parser.add_argument('file',
type=str,
nargs='*',
help='file or directory to include in multidiff')

parser.add_argument('-p','--port',
dest='port',
type=int,
help='start a local socket server on the given port')

parser.add_argument('-s','--stdin',
dest='stdin',
action='store_true',
Expand All @@ -52,29 +61,34 @@ def make_parser():
dest='mode',
default='sequence',
help='mode of operation, either "baseline" or "sequence"')

parser.add_argument('-i','--informat',
dest='informat',
help='input data format:\n' +
' utf8 (stdin default)\n' +
' raw (file and server default)\n' +
' hex\n'+
' json')

parser.add_argument('-o','--outformat',
dest='outformat',
default='hexdump',
help='output data format:\n' +
' utf8\n' +
' hex\n' +
help='output data format:\n' +
' utf8\n' +
' hex\n' +
' hexdump (default)')

parser.add_argument('--html',
dest='color',
action='store_const',
const='html',
default='ansi',
default='html',
help='use html for colors instead of ansi codes')

parser.add_argument('-w', '--width',
dest='width',
default='82',
help='number of bytes per line')
return parser

if __name__ == '__main__':
Expand Down

0 comments on commit 5afcf55

Please sign in to comment.