diff options
Diffstat (limited to 'src/nix-log2xml')
-rw-r--r-- | src/nix-log2xml/local.mk | 8 | ||||
-rw-r--r-- | src/nix-log2xml/log2html.xsl | 83 | ||||
-rw-r--r-- | src/nix-log2xml/log2xml.cc | 200 | ||||
-rw-r--r-- | src/nix-log2xml/logfile.css | 86 | ||||
-rw-r--r-- | src/nix-log2xml/mark-errors.xsl | 24 | ||||
-rw-r--r-- | src/nix-log2xml/treebits.js | 50 |
6 files changed, 451 insertions, 0 deletions
diff --git a/src/nix-log2xml/local.mk b/src/nix-log2xml/local.mk new file mode 100644 index 000000000000..46eb2e02ca64 --- /dev/null +++ b/src/nix-log2xml/local.mk @@ -0,0 +1,8 @@ +programs += nix-log2xml + +nix-log2xml_DIR := $(d) + +nix-log2xml_SOURCES := $(d)/log2xml.cc + +$(foreach file, mark-errors.xsl log2html.xsl treebits.js, \ + $(eval $(call install-data-in, $(d)/$(file), $(datadir)/nix/log2html))) diff --git a/src/nix-log2xml/log2html.xsl b/src/nix-log2xml/log2html.xsl new file mode 100644 index 000000000000..209399470640 --- /dev/null +++ b/src/nix-log2xml/log2html.xsl @@ -0,0 +1,83 @@ +<?xml version="1.0"?> + +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + + <xsl:output method='html' encoding="UTF-8" + doctype-public="-//W3C//DTD HTML 4.01//EN" + doctype-system="http://www.w3.org/TR/html4/strict.dtd" /> + + <xsl:template match="logfile"> + <html> + <head> + <script type="text/javascript" src="treebits.js" /> + <link rel="stylesheet" href="logfile.css" type="text/css" /> + <title>Log File</title> + </head> + <body> + <ul class='toplevel'> + <xsl:for-each select='line|nest'> + <li> + <xsl:apply-templates select='.'/> + </li> + </xsl:for-each> + </ul> + </body> + </html> + </xsl:template> + + + <xsl:template match="nest"> + + <!-- The tree should be collapsed by default if all children are + unimportant or if the header is unimportant. --> +<!-- <xsl:variable name="collapsed" + select="count(.//line[not(@priority = 3)]) = 0 or ./head[@priority = 3]" /> --> + <xsl:variable name="collapsed" select="count(.//*[@error]) = 0"/> + + <xsl:variable name="style"><xsl:if test="$collapsed">display: none;</xsl:if></xsl:variable> + <xsl:variable name="arg"><xsl:choose><xsl:when test="$collapsed">true</xsl:when><xsl:otherwise>false</xsl:otherwise></xsl:choose></xsl:variable> + + <script type='text/javascript'>showTreeToggle(<xsl:value-of select="$collapsed"/>)</script> + <xsl:apply-templates select='head'/> + + <!-- Be careful to only generate <ul>s if there are <li>s, otherwise it’s malformed. --> + <xsl:if test="line|nest"> + + <ul class='nesting' style="{$style}"> + <xsl:for-each select='line|nest'> + + <!-- Is this the last line? If so, mark it as such so that it + can be rendered differently. --> + <xsl:variable name="class"><xsl:choose><xsl:when test="position() != last()">line</xsl:when><xsl:otherwise>lastline</xsl:otherwise></xsl:choose></xsl:variable> + + <li class='{$class}'> + <span class='lineconn' /> + <span class='linebody'> + <xsl:apply-templates select='.'/> + </span> + </li> + </xsl:for-each> + </ul> + </xsl:if> + + </xsl:template> + + + <xsl:template match="head|line"> + <code> + <xsl:if test="@error"> + <xsl:attribute name="class">error</xsl:attribute> + </xsl:if> + <xsl:apply-templates/> + </code> + </xsl:template> + + + <xsl:template match="storeref"> + <em class='storeref'> + <span class='popup'><xsl:apply-templates/></span> + <span class='elided'>/...</span><xsl:apply-templates select='name'/><xsl:apply-templates select='path'/> + </em> + </xsl:template> + +</xsl:stylesheet> \ No newline at end of file diff --git a/src/nix-log2xml/log2xml.cc b/src/nix-log2xml/log2xml.cc new file mode 100644 index 000000000000..6645dc500fed --- /dev/null +++ b/src/nix-log2xml/log2xml.cc @@ -0,0 +1,200 @@ +#include <vector> +#include <iostream> +#include <cstdio> +#include <string> +#include <cstring> + +using namespace std; + + +struct Decoder +{ + enum { stTop, stEscape, stCSI } state; + string line; + bool inHeader; + int level; + vector<int> args; + bool newNumber; + int priority; + bool ignoreLF; + int lineNo, charNo; + bool warning; + bool error; + + Decoder() + { + state = stTop; + line = ""; + inHeader = false; + level = 0; + priority = 1; + ignoreLF = false; + lineNo = 1; + charNo = 0; + warning = false; + error = false; + } + + void pushChar(char c); + + void finishLine(); + + void decodeFile(istream & st); +}; + + +void Decoder::pushChar(char c) +{ + if (c == '\n') { + lineNo++; + charNo = 0; + } else charNo++; + + switch (state) { + + case stTop: + if (c == '\e') { + state = stEscape; + } else if (c == '\n' && !ignoreLF) { + finishLine(); + } else line += c; + break; + + case stEscape: + if (c == '[') { + state = stCSI; + args.clear(); + newNumber = true; + } else + state = stTop; /* !!! wrong */ + break; + + case stCSI: + if (c >= 0x40 && c != 0x7e) { + state = stTop; + switch (c) { + case 'p': + if (line.size()) finishLine(); + level++; + inHeader = true; + cout << "<nest>" << endl; + priority = args.size() >= 1 ? args[0] : 1; + break; + case 'q': + if (line.size()) finishLine(); + if (level > 0) { + level--; + cout << "</nest>" << endl; + } else + cerr << "not enough nesting levels at line " + << lineNo << ", character " << charNo << endl; + break; + case 's': + if (line.size()) finishLine(); + priority = args.size() >= 1 ? args[0] : 1; + break; + case 'a': + ignoreLF = true; + break; + case 'b': + ignoreLF = false; + break; + case 'e': + error = true; + break; + case 'w': + warning = true; + break; + } + } else if (c >= '0' && c <= '9') { + int n = 0; + if (!newNumber) { + n = args.back() * 10; + args.pop_back(); + } + n += c - '0'; + args.push_back(n); + } + break; + + } +} + + +void Decoder::finishLine() +{ + string storeDir = "/nix/store/"; + int sz = storeDir.size(); + string tag = inHeader ? "head" : "line"; + cout << "<" << tag; + if (priority != 1) cout << " priority='" << priority << "'"; + if (warning) cout << " warning='1'"; + if (error) cout << " error='1'"; + cout << ">"; + + for (unsigned int i = 0; i < line.size(); i++) { + + if (line[i] == '<') cout << "<"; + else if (line[i] == '&') cout << "&"; + else if (line[i] == '\r') ; /* ignore carriage return */ + else if (line[i] < 32 && line[i] != 9) cout << "�"; + else if (i + sz + 33 < line.size() && + string(line, i, sz) == storeDir && + line[i + sz + 32] == '-') + { + int j = i + sz + 32; + /* skip name */ + while (!strchr("/\n\r\t ()[]:;?<>", line[j])) j++; + int k = j; + while (!strchr("\n\r\t ()[]:;?<>", line[k])) k++; + // !!! escaping + cout << "<storeref>" + << "<storedir>" + << string(line, i, sz) + << "</storedir>" + << "<hash>" + << string(line, i + sz, 32) + << "</hash>" + << "<name>" + << string(line, i + sz + 32, j - (i + sz + 32)) + << "</name>" + << "<path>" + << string(line, j, k - j) + << "</path>" + << "</storeref>"; + i = k - 1; + } else cout << line[i]; + } + + cout << "</" << tag << ">" << endl; + line = ""; + inHeader = false; + priority = 1; + warning = false; + error = false; +} + + +void Decoder::decodeFile(istream & st) +{ + int c; + + cout << "<logfile>" << endl; + + while ((c = st.get()) != EOF) { + pushChar(c); + } + + if (line.size()) finishLine(); + + while (level--) cout << "</nest>" << endl; + + cout << "</logfile>" << endl; +} + + +int main(int argc, char * * argv) +{ + Decoder dec; + dec.decodeFile(cin); +} diff --git a/src/nix-log2xml/logfile.css b/src/nix-log2xml/logfile.css new file mode 100644 index 000000000000..ed390d64a9ef --- /dev/null +++ b/src/nix-log2xml/logfile.css @@ -0,0 +1,86 @@ +body { + font-family: sans-serif; + background: white; +} + + +ul.nesting, ul.toplevel { + padding: 0; + margin: 0; +} + +ul.toplevel { + list-style-type: none; +} + +ul.nesting li.line, ul.nesting li.lastline { + position: relative; + list-style-type: none; +} + +ul.nesting li.line { + padding-left: 1.1em; +} + +ul.nesting li.lastline { + padding-left: 1.2em; // for the 0.1em border-left in .lastline > .lineconn +} + +li.line { + border-left: 0.1em solid #6185a0; +} + +li.line > span.lineconn, li.lastline > span.lineconn { + position: absolute; + height: 0.65em; + left: 0em; + width: 1em; + border-bottom: 0.1em solid #6185a0; +} + +li.lastline > span.lineconn { + border-left: 0.1em solid #6185a0; +} + + +em.storeref { + color: #500000; + position: relative; + width: 100%; +} + +em.storeref:hover { + background-color: #eeeeee; +} + +*.popup { + display: none; +/* background: url('http://losser.st-lab.cs.uu.nl/~mbravenb/menuback.png') repeat; */ + background: #ffffcd; + border: solid #555555 1px; + position: absolute; + top: 0em; + left: 0em; + margin: 0; + padding: 0; + z-index: 100; +} + +em.storeref:hover span.popup { + display: inline; +} + + +.toggle { + text-decoration: none; +} + +.showTree, .hideTree { + font-family: monospace; + font-size: larger; +} + +.error { + color: #ff0000; + font-weight: bold; +} \ No newline at end of file diff --git a/src/nix-log2xml/mark-errors.xsl b/src/nix-log2xml/mark-errors.xsl new file mode 100644 index 000000000000..4e91913e570a --- /dev/null +++ b/src/nix-log2xml/mark-errors.xsl @@ -0,0 +1,24 @@ +<?xml version="1.0"?> + +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + + <xsl:template match="@*|node()"> + <xsl:copy> + <xsl:apply-templates select="@*|node()"/> + </xsl:copy> + </xsl:template> + + <xsl:template match="line"> + <line> + <xsl:if test="contains(text(), ' *** ') or + contains(text(), 'LaTeX Error') or + contains(text(), 'FAIL:') or + contains(text(), ' error: ') or + true"> + <xsl:attribute name="error"></xsl:attribute> + </xsl:if> + <xsl:apply-templates select="@*|node()"/> + </line> + </xsl:template> + +</xsl:stylesheet> \ No newline at end of file diff --git a/src/nix-log2xml/treebits.js b/src/nix-log2xml/treebits.js new file mode 100644 index 000000000000..3011b391d88b --- /dev/null +++ b/src/nix-log2xml/treebits.js @@ -0,0 +1,50 @@ +/* Acknowledgement: this is based on the Wikipedia table-of-contents + * toggle. */ + + +var idCounter = 0; + + +function showTreeToggle(isHidden) { + if (document.getElementById) { + var id = "toggle_" + idCounter; + document.writeln( + '<a href="javascript:toggleTree(\'' + id + '\')" class="toggle" id="' + id + '">' + + '<span class="showTree" ' + (isHidden ? '' : 'style="display: none;"') + '>+</span>' + + '<span class="hideTree" ' + (isHidden ? 'style="display: none;"' : '') + '>-</span>' + + '</a>'); + idCounter = idCounter + 1; + } +} + + +function toggleTree(id) { + + var href = document.getElementById(id); + + var node = href; + var tree = null; + while (node != null) { + if (node.className == "nesting") tree = node; + node = node.nextSibling; + } + + node = href.firstChild; + var hideTree = null; + var showTree = null; + while (node != null) { + if (node.className == "showTree") showTree = node; + else if (node.className == "hideTree") hideTree = node; + node = node.nextSibling; + } + + if (tree.style.display == 'none') { + tree.style.display = ''; + hideTree.style.display = ''; + showTree.style.display = 'none'; + } else { + tree.style.display = 'none'; + hideTree.style.display = 'none'; + showTree.style.display = ''; + } +} |