From nobody Tue May 13 11:54:27 2025 Delivered-To: importer@patchew.org Received-SPF: none (zoho.com: 206.53.239.180 is neither permitted nor denied by domain of freelists.org) client-ip=206.53.239.180; envelope-from=patchew-devel-bounce@freelists.org; helo=turing.freelists.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=none (zoho.com: 206.53.239.180 is neither permitted nor denied by domain of freelists.org) smtp.mailfrom=patchew-devel-bounce@freelists.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from turing.freelists.org (turing.freelists.org [206.53.239.180]) by mx.zohomail.com with SMTPS id 1520241399408856.2347554448638; Mon, 5 Mar 2018 01:16:39 -0800 (PST) Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id D7DC82716E; Mon, 5 Mar 2018 04:16:38 -0500 (EST) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1A0DShvLCUCM; Mon, 5 Mar 2018 04:16:38 -0500 (EST) Received: from turing.freelists.org (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 875C426402; Mon, 5 Mar 2018 04:16:38 -0500 (EST) Received: with ECARTIS (v1.0.0; list patchew-devel); Mon, 05 Mar 2018 04:16:38 -0500 (EST) Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 5082C2716E for ; Mon, 5 Mar 2018 04:16:38 -0500 (EST) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id QGv_gdECHEHf for ; Mon, 5 Mar 2018 04:16:38 -0500 (EST) Received: from mail-wm0-f68.google.com (mail-wm0-f68.google.com [74.125.82.68]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id DF59E26402 for ; Mon, 5 Mar 2018 04:16:37 -0500 (EST) Received: by mail-wm0-f68.google.com with SMTP id i3so13916067wmi.4 for ; Mon, 05 Mar 2018 01:16:37 -0800 (PST) Received: from donizetti.lan (94-36-191-219.adsl-ull.clienti.tiscali.it. [94.36.191.219]) by smtp.gmail.com with ESMTPSA id b68sm7018546wmi.30.2018.03.05.01.16.35 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 05 Mar 2018 01:16:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=freelists.org; s=turing; t=1520241398; bh=wlgGELUmas3VJ94E0TRg57wSeg/G/1Hbs0zEEi1fMQw=; h=From:To:Subject:Date:In-Reply-To:References:Reply-To:List-help: List-unsubscribe:List-Id:List-subscribe:List-owner:List-post: List-archive; b=D866JLpn8EKuiGRwUA6OWcnpxipd7zS7uaMp73utOakr2/906QBRkxPaY1o5C29KK vdZZ8fwbJT1VvzFgwoPITkKjQ+yEp9LjYnU1xhbkcxNbXHjCbDqK4Vt52f9oPiPMfn NdWQdcDqLDq7zqN/41Gb9Zgc8OrHiruuZl3fnirs= X-Virus-Scanned: Debian amavisd-new at turing.freelists.org DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=freelists.org; s=turing; t=1520241398; bh=wlgGELUmas3VJ94E0TRg57wSeg/G/1Hbs0zEEi1fMQw=; h=From:To:Subject:Date:In-Reply-To:References:Reply-To:List-help: List-unsubscribe:List-Id:List-subscribe:List-owner:List-post: List-archive; b=D866JLpn8EKuiGRwUA6OWcnpxipd7zS7uaMp73utOakr2/906QBRkxPaY1o5C29KK vdZZ8fwbJT1VvzFgwoPITkKjQ+yEp9LjYnU1xhbkcxNbXHjCbDqK4Vt52f9oPiPMfn NdWQdcDqLDq7zqN/41Gb9Zgc8OrHiruuZl3fnirs= X-Original-To: patchew-devel@freelists.org X-Virus-Scanned: Debian amavisd-new at turing.freelists.org X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:subject:date:message-id :in-reply-to:references; bh=xMux4HtlHc5yORzLzkJhaeITJ9S4pkotopj5COMdbWg=; b=V0Jrmx01/jOJ1tj+SGAZ8rK7lD+YFf0K4Izfy5DKJtde7cEnzfSRAUBeZObkZDJ0u8 rRjjGxKc+/HLFSSOOjvXbqLyFq70sKfV/kNHctpJxe9XxnOXmeSgLr0fVNB0bQBx5F3U AZwoxNP76RbM2ijHlulGIkxvPz0puMwU8xi8/rqcYGcDbRYE6Fes4XTYRBSYyT9XzeXZ T2SA6+OVKsELnqNsfk9AyJ7gNgi8BZ8JYGjxTNJ38wFImrdft3qLrwZMdA1wsk7Gez3f gyYNyRpPGdK2e+R9P51Q4y2Z6ixSl0KicJlM0jvGsR2Wdu3wxSjMc4S2m+oTtRIXCuyR TA7A== X-Gm-Message-State: AElRT7ETuosGC9HKyba0AHpqSOIvH6dI0dgdzK07TBvtb42WYfVifAGv WnI/smL/RYrVSfR1bEpIEe3VX6zj X-Google-Smtp-Source: AG47ELvpHt7PC/AZ4WfzWJ2nt7SLKh/F3kz2TVEiKEI6ZjmWgkCr6iv1kv641YvTbbi3mO/sa9Wcew== X-Received: by 10.28.29.209 with SMTP id d200mr8197735wmd.149.1520241396279; Mon, 05 Mar 2018 01:16:36 -0800 (PST) From: Paolo Bonzini To: patchew-devel@freelists.org Subject: [patchew-devel] [PATCH 2/6] ansi2html: create ANSIProcessor superclass Date: Mon, 5 Mar 2018 10:16:30 +0100 Message-Id: <20180305091634.7391-3-pbonzini@redhat.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20180305091634.7391-1-pbonzini@redhat.com> References: <20180305091634.7391-1-pbonzini@redhat.com> X-archive-position: 74 X-ecartis-version: Ecartis v1.0.0 Sender: patchew-devel-bounce@freelists.org Errors-to: patchew-devel-bounce@freelists.org X-original-sender: pbonzini@redhat.com Precedence: normal Reply-To: patchew-devel@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: patchew-devel X-List-ID: patchew-devel List-subscribe: List-owner: List-post: List-archive: X-list: patchew-devel X-ZohoMail-DKIM: pass (identity @freelists.org) X-ZohoMail: RDKM_0 RSF_4 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Paolo Bonzini --- patchew/logviewer.py | 216 ++++++++++++++++++++++++++++-------------------= ---- 1 file changed, 120 insertions(+), 96 deletions(-) diff --git a/patchew/logviewer.py b/patchew/logviewer.py index ab5f5c3..aa5d2bb 100644 --- a/patchew/logviewer.py +++ b/patchew/logviewer.py @@ -15,38 +15,18 @@ from django.views import View from django.http import HttpResponse, StreamingHttpResponse from django.utils.safestring import mark_safe =20 -class ANSI2HTMLConverter(object): +class ANSIProcessor(object): RE_STRING =3D '[^\b\t\n\f\r\x1B]+' RE_NUMS =3D '[0-9]+(?:;[0-9]+)*' RE_CSI =3D r'\[\??(?:' + RE_NUMS + ')?[^;0-9]' RE_OSC =3D r'].*?(?:\x1B\\|\x07)' RE_CONTROL =3D '\x1B(?:%s|%s|[^][])|\r\n|[\b\t\n\f\r]' % (RE_CSI, RE_O= SC) RE =3D re.compile('(%s)|(%s)' % (RE_STRING, RE_CONTROL)) - COLORS =3D [ "BLK", "RED", "GRN", "YEL", "BLU", "MAG", "CYN", "WHI", - "HIK", "HIR", "HIG", "HIY", "HIB", "HIM", "HIC", "HIW" ] - - ENTITIES =3D { - '\x00' : '␀', '\x01' : '␁', '\x02' : '␂', - '\x03' : '␃', '\x04' : '␄', '\x05' : '␅', - '\x06' : '␆', '\x07' : '🔔', '\x0B' : '␋', - '\x0E' : '␎', '\x0F' : '␏', '\x10' : '␐', - '\x11' : '␑', '\x12' : '␒', '\x13' : '␓', - '\x14' : '␔', '\x15' : '␕', '\x16' : '␖', - '\x17' : '␗', '\x18' : '␘', '\x19' : '␙', - '\x1A' : '␚', '\x1B' : '␛', '\x1C' : '␜', - '\x1D' : '␝', '\x1E' : '␞', '\x1F' : '␟', - '<' : '<', '>' : '>', '&' : '&', - '\x7F' : '⌦' - } - RE_ENTITIES =3D re.compile('[\x00-\x1F<>&\x7F]') =20 - def __init__(self, white_bg=3DFalse): + def __init__(self): self.class_to_id =3D {} self.id_to_class =3D [] - self.default_fg =3D 0 if white_bg else 7 - self.default_bg =3D 7 if white_bg else 0 self.cur_class =3D self._class_to_id("") - self.prefix =3D '
'
         self._reset()
         self._reset_attrs()
=20
@@ -99,17 +79,9 @@ class ANSI2HTMLConverter(object):
             self.line +=3D [' '] * num
             self.class_ids +=3D [0] * num
=20
-    def _write_prefix(self):
-        if self.prefix !=3D '':
-            yield self.prefix
-            self.prefix =3D ''
-
+    @abc.abstractmethod
     def _write_span(self, text, class_id):
-        if class_id > 0:
-            yield ('' % self.id_to_class[class_id])
-        yield self.RE_ENTITIES.sub(lambda x: self.ENTITIES[x.group(0)], te=
xt)
-        if class_id > 0:
-            yield ''
+        pass
=20
     # Flushing a line locates spans that have the same style, and prints t=
hose
     # with a  tag if they are styled.
@@ -198,7 +170,7 @@ class ANSI2HTMLConverter(object):
             self.bg =3D arg - 92
=20
     def _write_form_feed(self):
-        yield '
' + pass =20 def _class_to_id(self, html_class): class_id =3D self.class_to_id.get(html_class, None) @@ -208,67 +180,8 @@ class ANSI2HTMLConverter(object): self.id_to_class.append(html_class) return class_id =20 - def _map_color(self, color, default, dim): - # map a color assigned by _do_one_csi_m to an index in the COLORS = array - - color =3D color if color is not None else default - if dim: - # must be foreground color - if isinstance(color, int): - # unlike vte which has a "very dark" grey, for simplicity - # dark grey remains dark grey - return 8 if color =3D=3D default or color =3D=3D 8 else co= lor&~8 - else: - return ('d' + color[0], 'd' + color[1]) - else: - if isinstance(color, int): - # use light colors by default, except for black and light = grey - # (but see bold case in _compute_class) - return color if color =3D=3D 0 or color =3D=3D 7 else colo= r|8 - else: - return color - def _compute_class(self): - fg =3D self._map_color(self.fg, self.default_fg, self.dim) - bg =3D self._map_color(self.bg, self.default_bg, False) - - # apply inverse now: "inverse dim" affects the *background* color! - if self.inverse: - fg, bg =3D bg, fg - - # bold turns foreground light grey into white - if fg =3D=3D 7 and not self.dim and self.bold: - fg =3D 15 - - # now compute CSS classes - classes =3D [] - if isinstance(fg, int): - if fg !=3D self.default_fg: - classes.append(self.COLORS[fg]) - else: - # 256-color palette - classes.append(fg[0]) - - if isinstance(bg, int): - if bg !=3D self.default_bg: - classes.append('B' + self.COLORS[bg]) - else: - classes.append(bg[1]) - - if self.bold: - classes.append('BOLD') - if self.italic: - classes.append('ITA') - - if self.underline or self.strike: - undstr =3D '' - if self.underline: - undstr +=3D 'UND' - if self.strike: - undstr +=3D 'STR' - classes.append(undstr) - - self.cur_class =3D self._class_to_id(" ".join(classes)) + pass =20 def _do_csi_m(self, it): try: @@ -335,7 +248,6 @@ class ANSI2HTMLConverter(object): self._parse_csi_with_args(csi, self._do_csi_m) =20 def convert(self, input): - yield from self._write_prefix() for m in self.RE.finditer(input): if m.group(1): if self.lazy_accumulate: @@ -369,11 +281,123 @@ class ANSI2HTMLConverter(object): elif len(seq) > 1: yield from self._parse_csi(seq) =20 + def finish(self): + yield from self._write_line('') + self._reset_attrs() + + +class ANSI2HTMLConverter(ANSIProcessor): + ENTITIES =3D { + '\x00' : '␀', '\x01' : '␁', '\x02' : '␂', + '\x03' : '␃', '\x04' : '␄', '\x05' : '␅', + '\x06' : '␆', '\x07' : '🔔', '\x0B' : '␋', + '\x0E' : '␎', '\x0F' : '␏', '\x10' : '␐', + '\x11' : '␑', '\x12' : '␒', '\x13' : '␓', + '\x14' : '␔', '\x15' : '␕', '\x16' : '␖', + '\x17' : '␗', '\x18' : '␘', '\x19' : '␙', + '\x1A' : '␚', '\x1B' : '␛', '\x1C' : '␜', + '\x1D' : '␝', '\x1E' : '␞', '\x1F' : '␟', + '<' : '<', '>' : '>', '&' : '&', + '\x7F' : '⌦' + } + RE_ENTITIES =3D re.compile('[\x00-\x1F<>&\x7F]') + + COLORS =3D [ "BLK", "RED", "GRN", "YEL", "BLU", "MAG", "CYN", "WHI", + "HIK", "HIR", "HIG", "HIY", "HIB", "HIM", "HIC", "HIW" ] + + def __init__(self, white_bg=3DFalse): + super(ANSI2HTMLConverter, self).__init__() + self.default_fg =3D 0 if white_bg else 7 + self.default_bg =3D 7 if white_bg else 0 + self.prefix =3D '
'
+
+    def _write_prefix(self):
+        if self.prefix !=3D '':
+            yield self.prefix
+            self.prefix =3D ''
+
+    def _map_color(self, color, default, dim):
+        # map a color assigned by _do_one_csi_m to an index in the COLORS =
array
+
+        color =3D color if color is not None else default
+        if dim:
+            # must be foreground color
+            if isinstance(color, int):
+                # unlike vte which has a "very dark" grey, for simplicity
+                # dark grey remains dark grey
+                return 8 if color =3D=3D default or color =3D=3D 8 else co=
lor&~8
+            else:
+                return ('d' + color[0], 'd' + color[1])
+        else:
+            if isinstance(color, int):
+                # use light colors by default, except for black and light =
grey
+                # (but see bold case in _compute_class)
+                return color if color =3D=3D 0 or color =3D=3D 7 else colo=
r|8
+            else:
+                return color
+
+    def _compute_class(self):
+        fg =3D self._map_color(self.fg, self.default_fg, self.dim)
+        bg =3D self._map_color(self.bg, self.default_bg, False)
+
+        # apply inverse now: "inverse dim" affects the *background* color!
+        if self.inverse:
+            fg, bg =3D bg, fg
+
+        # bold turns foreground light grey into white
+        if fg =3D=3D 7 and not self.dim and self.bold:
+            fg =3D 15
+
+        # now compute CSS classes
+        classes =3D []
+        if isinstance(fg, int):
+            if fg !=3D self.default_fg:
+                classes.append(self.COLORS[fg])
+        else:
+            # 256-color palette
+            classes.append(fg[0])
+
+        if isinstance(bg, int):
+            if bg !=3D self.default_bg:
+                classes.append('B' + self.COLORS[bg])
+        else:
+            classes.append(bg[1])
+
+        if self.bold:
+            classes.append('BOLD')
+        if self.italic:
+            classes.append('ITA')
+
+        if self.underline or self.strike:
+            undstr =3D ''
+            if self.underline:
+                undstr +=3D 'UND'
+            if self.strike:
+                undstr +=3D 'STR'
+            classes.append(undstr)
+
+        self.cur_class =3D self._class_to_id(" ".join(classes))
+
+    def _write_span(self, text, class_id):
+        if class_id > 0:
+            yield ('' % self.id_to_class[class_id])
+        yield self.RE_ENTITIES.sub(lambda x: self.ENTITIES[x.group(0)], te=
xt)
+        if class_id > 0:
+            yield ''
+
+    def _write_form_feed(self):
+        yield '
' + + def convert(self, input): + yield from self._write_prefix() + yield from super(ANSI2HTMLConverter, self).convert(input) + def finish(self): yield from self._write_prefix() - yield from self._write_line('
') + yield from super(ANSI2HTMLConverter, self).finish() + yield '
' self.prefix =3D '
'
-        self._reset_attrs()
+
=20
 def ansi2html(input, white_bg=3DFalse):
     c =3D ANSI2HTMLConverter(white_bg=3Dwhite_bg)
--=20
2.14.3