mirror of
https://github.com/volatilityfoundation/volatility
synced 2026-06-08 18:04:46 +00:00
b69cb2a998
error:
self._elide(("{:<" + str(grid_max_widths[index]) + "}").format(column.name), grid_max_widths[index])]
ValueError: zero length field name in format
203 lines
7.6 KiB
Python
203 lines
7.6 KiB
Python
import math
|
|
import sys
|
|
from volatility import renderers
|
|
from volatility.fmtspec import FormatSpec
|
|
from volatility.renderers import ColumnSortKey
|
|
from volatility.renderers.basic import Address, Address64, Hex, Renderer
|
|
|
|
__author__ = 'mike'
|
|
|
|
class CellRenderer(object):
|
|
"""Class to handle rendering of a particular cell in a text grid"""
|
|
# The minimum width that the renderer will produce for a value
|
|
width = 0
|
|
|
|
def render(self, value):
|
|
"""Returns the rendering of an individual value"""
|
|
return value
|
|
|
|
|
|
class FormatCellRenderer(CellRenderer):
|
|
"""Class to handle rendering each cell of a grid"""
|
|
|
|
def __init__(self, format_spec):
|
|
if not isinstance(format_spec, FormatSpec):
|
|
fs = FormatSpec()
|
|
fs.from_string(format_spec)
|
|
format_spec = fs
|
|
self._format_spec = format_spec
|
|
|
|
def render(self, value):
|
|
"""Render an individual cell"""
|
|
return ("{0:" + str(self._format_spec) + "}").format(value)
|
|
|
|
@property
|
|
def width(self):
|
|
return self._format_spec.minwidth
|
|
|
|
@width.setter
|
|
def width(self, value):
|
|
self._format_spec.minwidth = max(value, self._format_spec.minwidth)
|
|
|
|
def __repr__(self):
|
|
return "<FormatCellRenderer (" + repr(self._format_spec) + ")>"
|
|
|
|
|
|
class TextRenderer(Renderer):
|
|
min_column_width = 5
|
|
|
|
def __init__(self, cell_renderers_func, max_width = 200, sort_column = None, config = None):
|
|
"""Accepts a cell_renderer function, an optional maximum width and optional sort column.
|
|
|
|
The signature of the cell_renderers_function is:
|
|
|
|
def cell_renderers(self, TreeGridcolumns):
|
|
return [cell_renderer, cell_renderer, ...]
|
|
"""
|
|
self._cell_renderers_func = cell_renderers_func
|
|
self._cell_renderers = None
|
|
self.max_width = max_width
|
|
self.sort_column = sort_column
|
|
self._config = config
|
|
|
|
def partition_width(self, widths):
|
|
"""Determines if the widths are over the maximum available space, and if so shrinks them"""
|
|
if math.fsum(widths) + (len(widths) - 1) > self.max_width:
|
|
remainder = (int(math.fsum(widths)) + (len(widths) - 1)) - self.max_width
|
|
# Take from the largest column first, eventually evening out
|
|
for i in range(remainder):
|
|
col_index = widths.index(max(widths))
|
|
widths[col_index] -= 1
|
|
return widths
|
|
|
|
def _elide(self, string, length):
|
|
"""Ensures that strings passed as value are returned no longer than max_width characters long, elided if necessary"""
|
|
if length == -1:
|
|
return string
|
|
if len(string) < length:
|
|
return (" " * (length - len(string))) + string
|
|
elif len(string) == length:
|
|
return string
|
|
else:
|
|
if length < self.min_column_width:
|
|
return string
|
|
even = ((length + 1) % 2)
|
|
length = (length - 3) / 2
|
|
return string[:length + even] + "..." + string[-length:]
|
|
|
|
|
|
def _validate_grid(self, grid):
|
|
if not isinstance(grid, renderers.TreeGrid):
|
|
raise TypeError("Grid must be of type TreeGrid")
|
|
self._cell_renderers = self._cell_renderers_func(grid.columns)
|
|
if not isinstance(self._cell_renderers, list):
|
|
raise TypeError("cell_renderers must be of type list")
|
|
for item in self._cell_renderers:
|
|
if not isinstance(item, CellRenderer):
|
|
raise TypeError("Items within the cell_renderers list must be of type CellRenderer")
|
|
|
|
def render(self, outfd, grid):
|
|
"""Renders a text grid based on the contents of each element"""
|
|
sort_key = None
|
|
if self.sort_column:
|
|
sort_key = ColumnSortKey(grid, self.sort_column).key
|
|
|
|
self._validate_grid(grid)# Determine number of columns
|
|
|
|
if self._config and self._config.VERBOSE:
|
|
qtr = QuickTextRenderer(self._cell_renderers_func)
|
|
output = sys.stdout
|
|
output.write("Immediate (verbose) output:\n")
|
|
qtr.render(output, grid)
|
|
output.write("\n")
|
|
output.flush()
|
|
|
|
grid_depth = grid.visit(None, lambda x, y: max(y, grid.path_depth(x)), 0)
|
|
|
|
# Determine max width of each column
|
|
grid_max_widths = [0] * len(grid.columns)
|
|
|
|
def gridwidth(node, accumulator = None):
|
|
for vindex in range(len(node.values)):
|
|
entry = self._cell_renderers[vindex].render(node.values[vindex])
|
|
accumulator[vindex] = max(len(entry), accumulator[vindex])
|
|
return accumulator
|
|
|
|
grid.visit(None, gridwidth, grid_max_widths)
|
|
if grid_depth > 1:
|
|
grid_max_widths = [grid_depth * 1] + grid_max_widths
|
|
|
|
# Figure out how to partition the available widths
|
|
new_grid_widths = self.partition_width(grid_max_widths)
|
|
|
|
# If the grid_max_widths have not been limited,
|
|
if new_grid_widths == grid_max_widths:
|
|
for i in range(len(grid.columns)):
|
|
index = i + (1 if grid_depth > 1 else 0)
|
|
grid_max_widths[index] = max(grid_max_widths[index], len(grid.columns[i].name))
|
|
|
|
for i in range(len(grid.columns)):
|
|
index = i + (1 if grid_depth > 1 else 0)
|
|
self._cell_renderers[i].width = grid_max_widths[index]
|
|
grid_max_widths[index] = self._cell_renderers[i].width
|
|
|
|
cols = []
|
|
for index in range(len(grid_max_widths)):
|
|
if grid_depth > 1:
|
|
if index == 0:
|
|
cols += [" " * grid_max_widths[index]]
|
|
continue
|
|
else:
|
|
column = grid.columns[index - 1]
|
|
else:
|
|
column = grid.columns[index]
|
|
cols += [
|
|
self._elide(("{0:<" + str(grid_max_widths[index]) + "}").format(column.name), grid_max_widths[index])]
|
|
outfd.write(" ".join(cols) + "\r\n")
|
|
|
|
def print_row(node, accumulator):
|
|
row = []
|
|
for index in range(len(grid_max_widths)):
|
|
if grid_depth > 1:
|
|
if index == 0:
|
|
row += [(" " * (grid.path_depth(node) - 1)) + ">" + (
|
|
" " * (grid_max_widths[0] - grid.path_depth(node)))]
|
|
continue
|
|
else:
|
|
column = grid.columns[index - 1]
|
|
else:
|
|
column = grid.columns[index]
|
|
|
|
column_text = self._cell_renderers[column.index].render(node.values[column.index])
|
|
row += [self._elide(column_text, grid_max_widths[index])]
|
|
accumulator += [" ".join(row)]
|
|
return accumulator
|
|
|
|
output = []
|
|
grid.visit(None, print_row, output, sort_key = sort_key)
|
|
outfd.write("\r\n".join(output) + "\r\n")
|
|
|
|
class QuickTextRenderer(TextRenderer):
|
|
def render(self, outfd, grid):
|
|
self._validate_grid(grid)
|
|
|
|
# Determine max width of each column
|
|
grid_max_widths = [0] * len(grid.columns)
|
|
|
|
# If the grid_max_widths have not been limited,
|
|
headers = []
|
|
for i in range(len(grid.columns)):
|
|
grid_max_widths[i] = max(grid_max_widths[i], len(grid.columns[i].name))
|
|
headers += [grid.columns[i].name]
|
|
outfd.write("|".join(headers) + "\n")
|
|
|
|
def print_row(node, outfd):
|
|
outfd.write(">" * grid.path_depth(node))
|
|
for column in grid.columns:
|
|
outfd.write("|" + self._cell_renderers[column.index].render(node.values[column.index]))
|
|
outfd.write("\n")
|
|
outfd.flush()
|
|
return outfd
|
|
|
|
grid.populate(print_row, outfd)
|