diff options
-rw-r--r-- | bin/hilex/Makefile | 1 | ||||
-rw-r--r-- | bin/hilex/hilex.1 | 54 | ||||
-rw-r--r-- | bin/hilex/hilex.c | 2 | ||||
-rw-r--r-- | bin/hilex/hilex.h | 9 | ||||
-rw-r--r-- | bin/hilex/html.c | 135 |
5 files changed, 200 insertions, 1 deletions
diff --git a/bin/hilex/Makefile b/bin/hilex/Makefile index aeaa36d4..5f6c1d80 100644 --- a/bin/hilex/Makefile +++ b/bin/hilex/Makefile @@ -3,6 +3,7 @@ CFLAGS += -std=c11 -Wall -Wextra -Wpedantic OBJS += ansi.o OBJS += c.o OBJS += hilex.o +OBJS += html.o OBJS += irc.o OBJS += mdoc.o OBJS += text.o diff --git a/bin/hilex/hilex.1 b/bin/hilex/hilex.1 index 8b8e0ec7..bc345fe1 100644 --- a/bin/hilex/hilex.1 +++ b/bin/hilex/hilex.1 @@ -65,6 +65,60 @@ input lexer if one cannot be inferred. .It Cm ansi Output ANSI terminal control sequences. . +.It Cm html +Output HTML +.Sy <span> +elements +within a +.Sy <pre> +element. +The options are as follows: +.Bl -tag -width "title=..." +.It Cm anchor +Output tags as anchor links. +. +.It Cm css Ns = Ns Ar url +With +.Cm document , +output a +.Sy <link> +element for the external stylesheet +.Ar url . +If unset, +output default styles in a +.Sy <style> +element. +. +.It Cm document +Output an HTML document containing the +.Sy <pre> +element. +. +.It Cm inline +Output inline +.Sy style +attributes rather than classes. +. +.It Cm tab Ns = Ns Ar n +With +.Cm document +or +.Cm inline , +set the +.Sy tab-size +property to +.Ar n . +. +.It Cm title Ns = Ns Ar ... +With +.Cm document , +set the +.Sy <title> +element text. +The default title is the same as +.Ar name . +.El +. .It Cm irc Output IRC formatting codes. The options are as follows: diff --git a/bin/hilex/hilex.c b/bin/hilex/hilex.c index 089f85ee..43130f4d 100644 --- a/bin/hilex/hilex.c +++ b/bin/hilex/hilex.c @@ -63,6 +63,7 @@ static const struct { } Formatters[] = { { &FormatANSI, "ansi" }, { &FormatDebug, "debug" }, + { &FormatHTML, "html" }, { &FormatIRC, "irc" }, }; @@ -135,6 +136,7 @@ int main(int argc, char *argv[]) { name = path; } } + if (!opts[Title]) opts[Title] = name; if (!lexer) lexer = matchLexer(name); if (!lexer && text) lexer = &LexText; if (!lexer) errx(EX_USAGE, "cannot infer lexer for %s", name); diff --git a/bin/hilex/hilex.h b/bin/hilex/hilex.h index f0e49a78..870c8a3a 100644 --- a/bin/hilex/hilex.h +++ b/bin/hilex/hilex.h @@ -51,7 +51,13 @@ extern const struct Lexer LexMdoc; extern const struct Lexer LexText; #define ENUM_OPTION \ - X(Monospace, "monospace") + X(Anchor, "anchor") \ + X(CSS, "css") \ + X(Document, "document") \ + X(Inline, "inline") \ + X(Monospace, "monospace") \ + X(Tab, "tab") \ + X(Title, "title") enum Option { #define X(option, key) option, @@ -70,4 +76,5 @@ struct Formatter { extern const struct Formatter FormatANSI; extern const struct Formatter FormatDebug; +extern const struct Formatter FormatHTML; extern const struct Formatter FormatIRC; diff --git a/bin/hilex/html.c b/bin/hilex/html.c new file mode 100644 index 00000000..cfa42770 --- /dev/null +++ b/bin/hilex/html.c @@ -0,0 +1,135 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "hilex.h" + +static void htmlEscape(const char *text) { + while (*text) { + switch (*text) { + break; case '"': text++; printf("""); + break; case '&': text++; printf("&"); + break; case '<': text++; printf("<"); + } + size_t len = strcspn(text, "\"&<"); + if (len) fwrite(text, len, 1, stdout); + text += len; + } +} + +static const char *Class[ClassCap] = { +#define X(class) [class] = #class, + ENUM_CLASS +#undef X +}; + +static const char *Style[ClassCap] = { + [Keyword] = "color: dimgray;", + [Tag] = "color: inherit;", + [Macro] = "color: green;", + [Comment] = "color: navy;", + [String] = "color: teal;", + [StringFormat] = "color: teal; font-weight: bold;", +}; + +static void styleTabSize(const char *tab) { + printf("-moz-tab-size: "); + htmlEscape(tab); + printf("; tab-size: "); + htmlEscape(tab); + printf(";"); +} + +static void htmlHeader(const char *opts[]) { + if (!opts[Document]) goto body; + + printf("<!DOCTYPE html>\n<title>"); + if (opts[Title]) htmlEscape(opts[Title]); + printf("</title>\n"); + + if (opts[CSS]) { + printf("<link rel=\"stylesheet\" href=\""); + htmlEscape(opts[CSS]); + printf("\">\n"); + } else if (!opts[Inline]) { + printf("<style>\n"); + if (opts[Tab]) { + printf("pre.hi { "); + styleTabSize(opts[Tab]); + printf(" }\n"); + } + for (enum Class class = 0; class < ClassCap; ++class) { + if (!Style[class]) continue; + printf(".hi.%s { %s }\n", Class[class], Style[class]); + } + if (opts[Anchor]) { + printf( + ".hi.%s:target { color: goldenrod; outline: none; }\n", + Class[Tag] + ); + } + printf("</style>\n"); + } + +body: + if (opts[Inline] && opts[Tab]) { + printf("<pre class=\"hi\" style=\""); + styleTabSize(opts[Tab]); + printf("\">"); + } else { + printf("<pre class=\"hi\">"); + } +} + +static void htmlFooter(const char *opts[]) { + printf("</pre>"); + if (opts[Document]) printf("\n"); +} + +static void htmlAnchor(const char *opts[], const char *text) { + if (opts[Inline]) { + printf("<a style=\"%s\" id=\"", Style[Tag]); + } else { + printf("<a class=\"hi %s\" id=\"", Class[Tag]); + } + htmlEscape(text); + printf("\" href=\"#"); + htmlEscape(text); + printf("\">"); + htmlEscape(text); + printf("</a>"); +} + +static void htmlFormat(const char *opts[], enum Class class, const char *text) { + if (opts[Anchor] && class == Tag) { + htmlAnchor(opts, text); + } else if (class == Normal) { + htmlEscape(text); + } else { + if (opts[Inline]) { + printf("<span style=\"%s\">", Style[class] ? Style[class] : ""); + } else { + printf("<span class=\"hi %s\">", Class[class]); + } + htmlEscape(text); + printf("</span>"); + } +} + +const struct Formatter FormatHTML = { htmlHeader, htmlFormat, htmlFooter }; |