diff options
author | bptato <nincsnevem662@gmail.com> | 2023-12-12 20:03:44 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-12-12 20:03:44 +0100 |
commit | 1dcb9cf6567145a0d3705b95717ccbf37eac6140 (patch) | |
tree | 8c592cb23a2ca942e9f1c378159c5620865ea322 /adapter/format | |
parent | 189e73f7092a69699f3a6ca39aa105d318baedd4 (diff) | |
download | chawan-1dcb9cf6567145a0d3705b95717ccbf37eac6140.tar.gz |
adapter/: re-structure
Diffstat (limited to 'adapter/format')
-rw-r--r-- | adapter/format/gmi2html.c | 241 | ||||
-rw-r--r-- | adapter/format/gopher2html.nim | 134 |
2 files changed, 375 insertions, 0 deletions
diff --git a/adapter/format/gmi2html.c b/adapter/format/gmi2html.c new file mode 100644 index 00000000..2c8c2bf6 --- /dev/null +++ b/adapter/format/gmi2html.c @@ -0,0 +1,241 @@ +/* This file is dedicated to the public domain. + * + * Convert gemtext to HTML. Only accepts input on stdin. + */ + +#include <stdio.h> +#include <stdlib.h> + +typedef enum { + STATE_NORMAL, + STATE_BLOCKQUOTE, + STATE_NEWLINE, + STATE_NEWLINE_EQUALS, + STATE_NEWLINE_EQUALS_ARROW, + STATE_BEFORE_URL, + STATE_IN_URL, + STATE_BEFORE_URL_NAME, + STATE_URL_NAME, + STATE_SINGLE_BACKTICK, + STATE_DOUBLE_BACKTICK, + STATE_PRE_START, + STATE_IN_PRE, + STATE_PRE_SINGLE_BACKTICK, + STATE_PRE_DOUBLE_BACKTICK, + STATE_SKIP_LINE, + STATE_HASH, + STATE_DOUBLE_HASH, + STATE_AFTER_HASH, + STATE_AFTER_DOUBLE_HASH, + STATE_AFTER_TRIPLE_HASH +} ParseState; + +static ParseState state = STATE_NEWLINE; +static ParseState prev_state = STATE_NORMAL; + +int main(void) { + int c; +#define BUFSIZE 4096 + char urlbuf[BUFSIZE + 1]; + char *urlp; + + urlp = urlbuf; + printf("<!DOCTYPE html>"); +#define SET_STATE(s) do { \ + prev_state = state; \ + state = s; \ + } while (0) +#define REDO_NORMAL do { \ + SET_STATE(STATE_NORMAL); \ + goto normal; \ + } while (0) + while ((c = getc(stdin)) != EOF) { + switch (state) { + case STATE_NORMAL: + case STATE_BLOCKQUOTE: + case STATE_IN_PRE: + case STATE_PRE_START: + case STATE_SKIP_LINE: + case STATE_URL_NAME: + case STATE_AFTER_HASH: + case STATE_AFTER_DOUBLE_HASH: + case STATE_AFTER_TRIPLE_HASH: +normal: switch (c) { + case '\r': break; + case '\n': + if (state == STATE_BLOCKQUOTE) { + fputs("</blockquote>", stdout); + } else if (state == STATE_PRE_START) { + fputs("\">", stdout); + SET_STATE(STATE_IN_PRE); + } else if (state == STATE_URL_NAME) { + fputs("</a>", stdout); + fputs("<br>", stdout); + } else if (state == STATE_AFTER_HASH) { + fputs("</h1>", stdout); + } else if (state == STATE_AFTER_DOUBLE_HASH) { + fputs("</h2>", stdout); + } else if (state == STATE_AFTER_TRIPLE_HASH) { + fputs("</h3>", stdout); + } else if (state == STATE_SKIP_LINE) { + } else { + fputs("<br>", stdout); + } + SET_STATE(STATE_NEWLINE); + break; + case '<': + fputs("<", stdout); + break; + case '>': + fputs(">", stdout); + break; + case '&': + fputs("&", stdout); + break; + default: + if (state != STATE_SKIP_LINE) + putchar(c); + break; + } + break; + case STATE_NEWLINE: + if (prev_state == STATE_IN_PRE) { + if (c == '`') { + SET_STATE(STATE_PRE_SINGLE_BACKTICK); + break; + } else { + SET_STATE(STATE_IN_PRE); + goto normal; + } + } + switch (c) { + case '=': + SET_STATE(STATE_NEWLINE_EQUALS); + break; + case '>': + SET_STATE(STATE_BLOCKQUOTE); + printf("<blockquote>"); + break; + case '`': + SET_STATE(STATE_SINGLE_BACKTICK); + break; + case '#': + SET_STATE(STATE_HASH); + break; + default: + REDO_NORMAL; + } + break; + case STATE_NEWLINE_EQUALS: + if (c == '>') { + SET_STATE(STATE_NEWLINE_EQUALS_ARROW); + } else { + putchar('='); + REDO_NORMAL; + } + break; + case STATE_NEWLINE_EQUALS_ARROW: + if (c == ' ') { + state = STATE_BEFORE_URL; + } else { + putchar('='); + REDO_NORMAL; + } + break; + case STATE_BEFORE_URL: + if (c == ' ') { + continue; + break; + } else { + fputs("<a href=\"", stdout); + SET_STATE(STATE_IN_URL); + urlp = urlbuf; + } + /* fall through */ + case STATE_IN_URL: + switch (c) { + case '"': + fputs("%22", stdout); + if (urlp < &urlbuf[BUFSIZE]) + *urlp++ = '"'; + break; + case ' ': + case '\t': + fputs("\">", stdout); + *urlp = '\0'; + SET_STATE(STATE_BEFORE_URL_NAME); + break; + case '\n': + *urlp = '\0'; + fputs("\">", stdout); + fputs(urlbuf, stdout); + fputs("</a><br>", stdout); + SET_STATE(STATE_NEWLINE); + break; + default: + if (urlp < &urlbuf[BUFSIZE] && c != '>' + && c != '<') + *urlp++ = c; + putchar(c); + } + break; + case STATE_BEFORE_URL_NAME: + if (c != ' ' && c != '\t') { + SET_STATE(STATE_URL_NAME); + goto normal; + } + break; + case STATE_SINGLE_BACKTICK: + case STATE_PRE_SINGLE_BACKTICK: + if (c == '`') { + SET_STATE(state == STATE_SINGLE_BACKTICK ? + STATE_DOUBLE_BACKTICK : + STATE_PRE_DOUBLE_BACKTICK); + } else { + putchar('`'); + REDO_NORMAL; + } + break; + case STATE_DOUBLE_BACKTICK: + case STATE_PRE_DOUBLE_BACKTICK: + if (c == '`') { + if (state == STATE_DOUBLE_BACKTICK) { + SET_STATE(STATE_PRE_START); + fputs("<pre title=\"", stdout); + } else { + fputs("</pre>", stdout); + SET_STATE(STATE_SKIP_LINE); + } + } else { + fputs("``", stdout); + if (state == STATE_DOUBLE_BACKTICK) { + REDO_NORMAL; + } else { + SET_STATE(STATE_IN_PRE); + goto normal; + } + } + break; + case STATE_HASH: + if (c == '#') { + SET_STATE(STATE_DOUBLE_HASH); + } else { + fputs("<h1>", stdout); + SET_STATE(STATE_AFTER_HASH); + goto normal; + } + break; + case STATE_DOUBLE_HASH: + if (c == '#') { + fputs("<h3>", stdout); + SET_STATE(STATE_AFTER_TRIPLE_HASH); + } else { + fputs("<h2>", stdout); + SET_STATE(STATE_AFTER_DOUBLE_HASH); + goto normal; + } + break; + } + } + exit(0); +} diff --git a/adapter/format/gopher2html.nim b/adapter/format/gopher2html.nim new file mode 100644 index 00000000..cfdeab1e --- /dev/null +++ b/adapter/format/gopher2html.nim @@ -0,0 +1,134 @@ +# This file is dedicated to the public domain. +# Gopher directory -> HTML converter for Chawan. + +import std/os +import std/streams +import std/strutils + +import utils/twtstr + +type GopherType = enum + UNKNOWN = "unsupported" + TEXT_FILE = "text file" + ERROR = "error" + DIRECTORY = "directory" + DOS_BINARY = "DOS binary" + SEARCH = "search" + MESSAGE = "message" + SOUND = "sound" + GIF = "gif" + HTML = "HTML" + INFO = "" + IMAGE = "image" + BINARY = "binary" + PNG = "png" + +func gopherType(c: char): GopherType = + return case c + of '0': TEXT_FILE + of '1': DIRECTORY + of '3': ERROR + of '5': DOS_BINARY + of '7': SEARCH + of 'm': MESSAGE + of 's': SOUND + of 'g': GIF + of 'h': HTML + of 'i': INFO + of 'I': IMAGE + of '9': BINARY + of 'p': PNG + else: UNKNOWN + +const ControlPercentEncodeSet = {char(0x00)..char(0x1F), char(0x7F)..char(0xFF)} +const QueryPercentEncodeSet = (ControlPercentEncodeSet + {' ', '"', '#', '<', '>'}) +const PathPercentEncodeSet = (QueryPercentEncodeSet + {'?', '`', '{', '}'}) +const HexCharsUpper = "0123456789ABCDEF" +proc percentEncode(s: string): string = + result = "" + for c in s: + if c notin PathPercentEncodeSet: + result &= c + else: + result &= '%' + result &= HexCharsUpper[uint8(c) shr 4] + result &= HexCharsUpper[uint8(c) and 0xF] + +# returns URL +proc parseParams(): string = + result = "" + let params = commandLineParams() + var was_u = false + for param in params: + if was_u: + result = param + was_u = false + elif param == "-h" or param == "--help": + stdout.write("Usage: gopher2html [-u URL]") + quit(0) + elif param == "-u": + was_u = true + else: + stdout.write("Usage: gopher2html [-u URL]") + quit(1) + +proc main() = + let url = parseParams() + let escapedURL = htmlEscape(url) + stdout.write(""" +<!DOCTYPE HTML> +<HTML> +<HEAD> +<BASE HREF="""" & url & """"> +<TITLE>Index of """ & escapedURL & """</TITLE> +</HEAD> +<BODY> +<H1>Index of """ & escapedURL & """</H1>""") + let ins = newFileStream(stdin) + var ispre = false + while not ins.atEnd: + let line = ins.readLine() + if line.len == 0: + break # invalid + let tc = line[0] + if tc == '.': + break # end + var i = 1 + template get_field(): string = + let s = line.until('\t', i) + i += s.len + if i >= line.len or line[i] != '\t': + break # invalid + inc i + s + let t = gopherType(tc) + let name = get_field() + var file = get_field() + let host = get_field() + let port = line.until('\t', i) # ignore anything after port + var outs = "" + if t == INFO: + if not ispre: + outs &= "<PRE>" + ispre = true + outs &= htmlEscape(name) & '\n' + else: + if ispre: + outs &= "</PRE>" + ispre = false + let ts = $t + var names = "" + if ts != "": + names &= '[' & ts & ']' + names &= htmlEscape(name) + let ourls = if not file.startsWith("URL:"): + if file.len == 0 or file[0] != '/': + file = '/' & file + let pefile = percentEncode(file) + "gopher://" & host & ":" & port & "/" & tc & pefile + else: + file.substr("URL:".len) + outs &= "<A HREF=\"" & htmlEscape(ourls) & "\">" & names & "</A><BR>\n" + stdout.write(outs) + +main() |