Parse ESC[m CSIs, except those for colors.
Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
patchew/logviewer.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++--
static/css/ansi2html.css | 9 ++++++
tests/test_ansi2html.py | 39 ++++++++++++++++++++++++
3 files changed, 123 insertions(+), 2 deletions(-)
create mode 100644 static/css/ansi2html.css
diff --git a/patchew/logviewer.py b/patchew/logviewer.py
index b8d558d..8114eb6 100644
--- a/patchew/logviewer.py
+++ b/patchew/logviewer.py
@@ -4,7 +4,8 @@
#
# Author: Paolo Bonzini <pbonzini@xxxxxxxxxx>
-# Entity table based on ansi2html.c from colorized-logs.
+# Entity table and basic ESC[m parsing based on ansi2html.c from
+# colorized-logs.
import re
import abc
@@ -43,6 +44,16 @@ class ANSI2HTMLConverter(object):
self.cur_class = self._class_to_id("")
self.prefix = '<pre class="ansi">'
self._reset()
+ self._reset_attrs()
+
+ def _reset_attrs(self):
+ self.dim = 0
+ self.bold = 0
+ self.italic = 0
+ self.underline = 0
+ self.blink = 0
+ self.inverse = 0
+ self.strike = 0
def _reset(self):
self.line = []
@@ -118,6 +129,39 @@ class ANSI2HTMLConverter(object):
yield suffix
self._reset()
+ def _do_one_csi_m(self, it):
+ arg = next(it)
+ if arg < 10:
+ if arg == 0:
+ self._reset_attrs()
+ elif arg == 1:
+ self.bold = 1
+ elif arg == 2:
+ self.dim = 1
+ elif arg == 3:
+ self.italic = 1
+ elif arg == 4:
+ self.underline = 1
+ elif arg == 5:
+ self.blink = 1
+ elif arg == 7:
+ self.inverse = 1
+ elif arg == 9:
+ self.strike = 1
+ elif arg < 30:
+ if arg == 21 or arg == 22:
+ self.bold = self.dim = 0
+ elif arg == 23:
+ self.italic = 0
+ elif arg == 24:
+ self.underline = 0
+ elif arg == 25:
+ self.blink = 0
+ elif arg == 27:
+ self.inverse = 0
+ elif arg == 29:
+ self.strike = 0
+
def _class_to_id(self, html_class):
class_id = self.class_to_id.get(html_class, None)
if class_id is None:
@@ -126,6 +170,31 @@ class ANSI2HTMLConverter(object):
self.id_to_class.append(html_class)
return class_id
+ def _compute_class(self):
+ classes = []
+ if self.bold:
+ classes.append('BOLD')
+ if self.italic:
+ classes.append('ITA')
+
+ if self.underline or self.strike:
+ undstr = ''
+ if self.underline:
+ undstr += 'UND'
+ if self.strike:
+ undstr += 'STR'
+ classes.append(undstr)
+
+ self.cur_class = self._class_to_id(" ".join(classes))
+
+ def _do_csi_m(self, it):
+ try:
+ while True:
+ self._do_one_csi_m(it)
+ except StopIteration:
+ pass
+ self._compute_class()
+
def _do_csi_C(self, it):
arg = next(it)
if arg == 0:
@@ -175,6 +244,8 @@ class ANSI2HTMLConverter(object):
self._parse_csi_with_args(csi, self._do_csi_C)
elif csi[-1] == 'D':
self._parse_csi_with_args(csi, self._do_csi_D)
+ elif csi[-1] == 'm':
+ self._parse_csi_with_args(csi, self._do_csi_m)
def convert(self, input):
yield from self._write_prefix()
@@ -214,6 +285,7 @@ class ANSI2HTMLConverter(object):
yield from self._write_prefix()
yield from self._write_line('</pre>')
self.prefix = '<pre class="ansi">'
+ self._reset_attrs()
def ansi2html(input, white_bg=False):
c = ANSI2HTMLConverter(white_bg=white_bg)
@@ -236,8 +308,9 @@ class LogView(View, metaclass=abc.ABCMeta):
# consume more memory because we would not be able to just
# "yield from" into the StreamingHttpResponse.
HTML_PROLOG = mark_safe("""<!DOCTYPE html><html><head>
+<link rel="stylesheet" href="/static/css/ansi2html.css">
<style type="text/css">*{margin:0px;padding:0px;background:#333}
-pre { font-size: 13px; line-height: 1.42857143; white-space: pre-wrap;
word-wrap: break-word; max-width: 100%; color: #eee}
+pre { font-size: 13px; line-height: 1.42857143; white-space: pre-wrap;
word-wrap: break-word; max-width: 100%;}
</style>
<script type="text/javascript">
if (parent.jQuery && parent.jQuery.colorbox) {
diff --git a/static/css/ansi2html.css b/static/css/ansi2html.css
new file mode 100644
index 0000000..fa656a6
--- /dev/null
+++ b/static/css/ansi2html.css
@@ -0,0 +1,9 @@
+/* For black and white background respectively: */
+.ansi {color:#e8e2d2}
+/* .ansi {color:#000} */
+
+.ansi>.BOLD {font-weight: bold}
+.ansi>.ITA {font-style: italic}
+.ansi>.UND {text-decoration: underline}
+.ansi>.STR {text-decoration: line-through}
+.ansi>.UNDSTR {text-decoration: underline line-through}
diff --git a/tests/test_ansi2html.py b/tests/test_ansi2html.py
index ffbc98e..f0ee0ad 100644
--- a/tests/test_ansi2html.py
+++ b/tests/test_ansi2html.py
@@ -86,6 +86,45 @@ class ANSI2HTMLTest(unittest.TestCase):
self.assertBlackBg('abcd\r\x1b[2KDef', 'Def')
self.assertBlackBg('abcd\b\x1b[2KDef', ' Def')
+ # basic style formatting and bold
+ def test_basic_styles(self):
+ self.assertBlackBg('\x1b[0m', '')
+ self.assertWhiteBg('\x1b[0m', '')
+ self.assertBlackBg('A\x1b[0mBC', 'ABC')
+ self.assertWhiteBg('A\x1b[0mBC', 'ABC')
+ self.assertBlackBg('\x1b[30;41m', '')
+ self.assertWhiteBg('\x1b[30;41m', '')
+ self.assertBlackBg('\x1b[1mABC', '<span class="BOLD">ABC</span>')
+ self.assertWhiteBg('\x1b[1mABC', '<span class="BOLD">ABC</span>')
+ self.assertBlackBg('A\x1b[1mBC', 'A<span class="BOLD">BC</span>')
+ self.assertWhiteBg('A\x1b[1mBC', 'A<span class="BOLD">BC</span>')
+ self.assertBlackBg('\x1b[1mAB\x1b[0mC', '<span
class="BOLD">AB</span>C')
+ self.assertWhiteBg('\x1b[1mAB\x1b[0mC', '<span
class="BOLD">AB</span>C')
+ self.assertBlackBg('A\x1b[1mB\x1b[0mC', 'A<span
class="BOLD">B</span>C')
+ self.assertWhiteBg('A\x1b[1mB\x1b[0mC', 'A<span
class="BOLD">B</span>C')
+ self.assertBlackBg('A\x1b[1mB\x1b[0m\x1b[1mC', 'A<span
class="BOLD">BC</span>')
+ self.assertWhiteBg('A\x1b[1mB\x1b[0m\x1b[1mC', 'A<span
class="BOLD">BC</span>')
+
+ # italic, underline, strikethrough
+ def test_text_variants(self):
+ self.assertBlackBg('\x1b[3mABC', '<span class="ITA">ABC</span>')
+ self.assertWhiteBg('\x1b[3mABC', '<span class="ITA">ABC</span>')
+ self.assertBlackBg('\x1b[3mAB\x1b[23mC', '<span
class="ITA">AB</span>C')
+ self.assertWhiteBg('\x1b[3mAB\x1b[23mC', '<span
class="ITA">AB</span>C')
+ self.assertBlackBg('\x1b[4mABC', '<span class="UND">ABC</span>')
+ self.assertWhiteBg('\x1b[4mABC', '<span class="UND">ABC</span>')
+ self.assertBlackBg('\x1b[4mAB\x1b[24mC', '<span
class="UND">AB</span>C')
+ self.assertWhiteBg('\x1b[4mAB\x1b[24mC', '<span
class="UND">AB</span>C')
+ self.assertBlackBg('\x1b[9mABC', '<span class="STR">ABC</span>')
+ self.assertWhiteBg('\x1b[9mABC', '<span class="STR">ABC</span>')
+ self.assertBlackBg('\x1b[9mAB\x1b[29mC', '<span
class="STR">AB</span>C')
+ self.assertWhiteBg('\x1b[9mAB\x1b[29mC', '<span
class="STR">AB</span>C')
+ self.assertBlackBg('\x1b[4;9mABC', '<span class="UNDSTR">ABC</span>')
+ self.assertWhiteBg('\x1b[4;9mABC', '<span class="UNDSTR">ABC</span>')
+ self.assertBlackBg('\x1b[4;9mAB\x1b[24mC', '<span
class="UNDSTR">AB</span><span class="STR">C</span>')
+ self.assertWhiteBg('\x1b[4;9mAB\x1b[24mC', '<span
class="UNDSTR">AB</span><span class="STR">C</span>')
+ self.assertBlackBg('\x1b[4;9mAB\x1b[29mC', '<span
class="UNDSTR">AB</span><span class="UND">C</span>')
+ self.assertWhiteBg('\x1b[4;9mAB\x1b[29mC', '<span
class="UNDSTR">AB</span><span class="UND">C</span>')
if __name__ == '__main__':
unittest.main()
--
2.14.3