about summary refs log tree commit diff stats
path: root/adapter/format
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-12-12 20:03:44 +0100
committerbptato <nincsnevem662@gmail.com>2023-12-12 20:03:44 +0100
commit1dcb9cf6567145a0d3705b95717ccbf37eac6140 (patch)
tree8c592cb23a2ca942e9f1c378159c5620865ea322 /adapter/format
parent189e73f7092a69699f3a6ca39aa105d318baedd4 (diff)
downloadchawan-1dcb9cf6567145a0d3705b95717ccbf37eac6140.tar.gz
adapter/: re-structure
Diffstat (limited to 'adapter/format')
-rw-r--r--adapter/format/gmi2html.c241
-rw-r--r--adapter/format/gopher2html.nim134
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("&lt;", stdout);
+				break;
+			case '>':
+				fputs("&gt;", stdout);
+				break;
+			case '&':
+				fputs("&amp;", 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()