summary refs log tree commit diff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c218
1 files changed, 163 insertions, 55 deletions
diff --git a/main.c b/main.c
index e749d58..0cddf91 100644
--- a/main.c
+++ b/main.c
@@ -12,6 +12,19 @@
 #include "arena.h"
 #include "args.h"
 
+typedef struct {
+	int standalone;
+	int from_stdin;
+	Str stylesheet;
+	int inlinep;
+	int csslink;
+	Str hvarv[1024];
+	Str title;
+	int hvarc;
+} Options;
+
+Options opts = { 0 };
+
 #define ARENA(n, sz) Arena n; { static char arena_backarr[sz];\
 	n.beg = arena_backarr; n.end = arena_backarr + sizeof(arena_backarr);\
 	__asm("":"+r"(n.beg)); __asm("":"+r"(n.end)); }
@@ -65,15 +78,18 @@ void str_cat_uri(Str *s, Str uri, Arena *a) {
 	str_catc(s, '\'', a);
 }
 
+void str_catc_html(Str *s, char c, Arena *a) {
+	switch (c) {
+	case '&': str_cat(s, S("&"), a); break;
+	case '<': str_cat(s, S("&lt;"), a); break;
+	case '>': str_cat(s, S("&gt;"), a); break;
+	default: str_catc(s, c, a); break;
+	}
+}
+
 void str_cat_html(Str *s, Str uri, Arena *a) {
 	for (isize i = 0; i < uri.n; i++) {
-		char c = uri.s[i];
-		switch (c) {
-		case '&': str_cat(s, S("&amp;"), a); break;
-		case '<': str_cat(s, S("&lt;"), a); break;
-		case '>': str_cat(s, S("&gt;"), a); break;
-		default: str_catc(s, c, a); break;
-		}
+		str_catc_html(s, uri.s[i], a);
 	}
 }
 
@@ -214,25 +230,24 @@ BlockList blk_gather(Str src, Arena *perm) {
 	Line *lptr = NULL;
 	while (next_line(&src, &line)) {
 		LineType t = classify_line(line, last);
-		if (last == LN_CODE) {
-			if (t == LN_CODE) last = LN_NONE;
-		} else if (t == LN_CODE) {
-			last = LN_CODE;
-		} else {
-			Block *b = blk.len > 0 ? &blk.data[blk.len-1] : NULL;
-			if (!b || t != b->type) {
-				b = blk_push(&blk, &lptr, t, perm);
-			}
-			Line *l = new(perm, Line);
-			LineType nt = line_init(l, line, t);
-			if (b->type != nt) {
-				if (b->lines) b = blk_push(&blk, &lptr, nt, perm);
-				else b->type = nt;
-			}
-			if (lptr) lptr->next = l;
-			lptr = l;
-			if (!b->lines) b->lines = lptr;
+		if (t == LN_CODE) {
+			last = last == LN_CODE ? LN_NONE : LN_CODE;
+			continue;
+		}
+		if (last == LN_CODE) t = LN_CODE;
+		Block *b = blk.len > 0 ? &blk.data[blk.len-1] : NULL;
+		if (!b || t != b->type) {
+			b = blk_push(&blk, &lptr, t, perm);
+		}
+		Line *l = new(perm, Line);
+		LineType nt = line_init(l, line, t);
+		if (b->type != nt) {
+			if (b->lines) b = blk_push(&blk, &lptr, nt, perm);
+			else b->type = nt;
 		}
+		if (lptr) lptr->next = l;
+		lptr = l;
+		if (!b->lines) b->lines = lptr;
 		last = t;
 	}
 	for (size_t i = 0; i < blk.len; i++) {
@@ -247,11 +262,103 @@ BlockList blk_gather(Str src, Arena *perm) {
 }
 
 #define O(s) str_cat_html(out, s, perm)
+#define Oi(s) markup_inline(&ist, out, s, scratch, perm)
 #define Os(s) str_cat(out, S(s), perm)
-#define Ot(a, s, b) Os(a), O(s), Os(b)
+#define Ot(a, s, b) Os(a), Oi(s), Os(b)
 #define Otl(a, f, b) for (Line *l = blk->lines; l; l = l->next) Ot(a, f, b)
 
-void str_cat_blk(Str *out, Block *blk, Arena *perm, Arena *scratch) {
+typedef enum {
+	IM_NONE,
+	IM_ITAL,
+	IM_CODE
+} InlineMarkup;
+
+typedef struct {
+	int stkc;
+	InlineMarkup *stkv;
+} InlineState;
+
+InlineMarkup im_last(InlineState *ms) {
+	if (ms->stkc > 0) {
+		return ms->stkv[ms->stkc - 1];
+	} else {
+		return IM_NONE;
+	}
+}
+
+void im_cat_op(Str *out, InlineMarkup mu, Arena *perm) {
+	switch (mu) {
+	case IM_ITAL: str_cat(out, S("<em>"), perm); break;
+	case IM_CODE: str_cat(out, S("<code>"), perm); break;
+	default: break;
+	}
+}
+
+void im_cat_cl(Str *out, InlineMarkup mu, Arena *perm) {
+	switch (mu) {
+	case IM_ITAL: str_cat(out, S("</em>"), perm); break;
+	case IM_CODE: str_cat(out, S("</code>"), perm); break;
+	default: break;
+	}
+}
+
+InlineMarkup im_pop(InlineState *ms) {
+	if (ms->stkc > 0) return ms->stkv[--ms->stkc];
+	return IM_NONE;
+}
+
+InlineMarkup im_push(InlineState *ms, InlineMarkup mu, Arena *scratch) {
+	if (ms->stkc > 0) {
+		ms->stkv = resize(scratch, ms->stkv, ms->stkc, ms->stkc + 1);
+	} else {
+		ms->stkv = new(scratch, InlineMarkup);
+	}
+	ms->stkv[ms->stkc++] = mu;
+	return mu;
+}
+
+void im_op(InlineState *ms, Str *out, InlineMarkup mu, Arena *scratch, Arena *perm) {
+	im_cat_op(out, im_push(ms, mu, scratch), perm);
+}
+
+void im_cl(InlineState *ms, Str *out, Arena *perm) {
+	im_cat_cl(out, im_pop(ms), perm);
+}
+
+void markup_inline(InlineState *ms, Str *out, Str src, Arena *scratch, Arena *perm) {
+	if (!opts.inlinep) {
+		str_cat_html(out, src, perm);
+		return;
+	}
+	for (int i = 0; i < src.n; i++) {
+		char c = src.s[i];
+		if (im_last(ms) == IM_CODE) {
+			if (c == '`') {
+				im_cl(ms, out, perm);
+			} else {
+				str_catc_html(out, src.s[i], perm);
+			}
+		} else {
+			if (c == '`') {
+				im_op(ms, out, IM_CODE, scratch, perm);
+			} else if (c == '*') {
+				if (im_last(ms) == IM_ITAL) {
+					im_cl(ms, out, perm);
+				} else {
+					im_op(ms, out, IM_ITAL, scratch, perm);
+				}
+			} else if (str_starts(str_skip(src, i), S("---"))) {
+				str_cat(out, S("&mdash;"), perm);
+				i += 2;
+			} else {
+				str_catc_html(out, src.s[i], perm);
+			}
+		}
+	}
+}
+
+void markup_block(Str *out, Block *blk, Arena *perm, Arena *scratch) {
+	InlineState ist = { 0 };
 	switch (blk->type) {
 	case LN_CODE:
 		Os("<pre><code>");
@@ -270,7 +377,13 @@ void str_cat_blk(Str *out, Block *blk, Arena *perm, Arena *scratch) {
 					: str_replace_end(l->url,
 						S(".gmi"), S(".html"),
 						scratch), perm);
-			Ot(">", l->txt.n > 0 ? l->txt : l->url, "</a></li>\n");
+			str_catc(out, '>', perm);
+			if (l->txt.n > 0) {
+				markup_inline(&ist, out, l->txt, scratch, perm);
+			} else {
+				str_cat_html(out, l->url, perm);
+			}
+			str_cat(out, S("</a></li>\n"), perm);
 		}
 		Os("</ul>");
 		break;
@@ -315,13 +428,14 @@ void str_cat_blk(Str *out, Block *blk, Arena *perm, Arena *scratch) {
 	case LN_PAR:
 		Os("<p>");
 		for (Line *l = blk->lines; l; l = l->next) {
-			O(l->txt);
+			Oi(l->txt);
 			if (l->next) Os("<br>\n");
 		}
 		Os("</p>");
 		break;
 	}
 	Os("\n");
+	while (ist.stkc > 0) im_cl(&ist, out, perm);
 }
 
 int wdoc(FILE *f, Doc **dp, Arena *perm, Arena *scratch) {
@@ -333,7 +447,7 @@ int wdoc(FILE *f, Doc **dp, Arena *perm, Arena *scratch) {
 		if (blk.data[i].type == LN_HDR1 && !d->title.s) {
 			d->title = blk.data[i].lines->txt;
 		}
-		str_cat_blk(&d->html, &blk.data[i], perm, scratch);
+		markup_block(&d->html, &blk.data[i], perm, scratch);
 		if (i + 1 < blk.len) str_cat(&d->html, S("\n"), perm);
 	}
 	*dp = d;
@@ -369,7 +483,7 @@ int hvar_calc(Str param, Str *name, Str *val, Str filename) {
 
 void usage(const char *cmd) {
 	fprintf(stderr, "usage: %s -?\n"
-			"       %s [-s] [[-l] -c FILE] [-t TITLE] [-h NAME:ARG1[,ARG2,ARG3...]] [FILES...]\n"
+			"       %s [-s] [-i] [[-l] -c FILE] [-t TITLE] [-h NAME:ARG1[,ARG2,ARG3...]] [FILES...]\n"
 			"\n"
 			" -? --help         show this help text\n"
 			" -s --standalone   prefix with html metadata\n"
@@ -378,37 +492,28 @@ void usage(const char *cmd) {
 			" -c --css          embed the given file within a <style> element\n"
 			" -l --link         when combined with --css, link to an external stylehseet\n"
 			"                   instead of reading from a file locally\n"
+			" -i --inline       enable inline formatting (*italics*, `code`, ---dashes)\n"
 			" -t --title        set the document title (does nothing without --standalone)\n",
 			cmd, cmd);
 }
 
-typedef struct {
-	int standalone;
-	int from_stdin;
-	Str stylesheet;
-	int csslink;
-	Str hvarv[1024];
-	Str title;
-	int hvarc;
-} Options;
-
-Str html_head(Options *o, Arena *m, Arena *l) {
+Str html_head(Arena *m, Arena *l) {
 	Str h = S("<!DOCTYPE html>\n"
 			"<meta charset=utf-8>\n"
 			"<meta name=viewport content='width=device-width,initial-scale=1'>\n");
 
-	if (o->title.s) {
+	if (opts.title.s) {
 		str_cat(&h, S("<title>"), m);
-		str_cat_html(&h, o->title, m);
+		str_cat_html(&h, opts.title, m);
 		str_cat(&h, S("</title>\n"), m);
 	}
 
-	if (o->hvarc > 0) {
+	if (opts.hvarc > 0) {
 		str_cat(&h, S("<style>\n"), m);
 		str_cat(&h, S(":root {"), m);
-		for (int i = 0; i < o->hvarc; i++) {
+		for (int i = 0; i < opts.hvarc; i++) {
 			Str name, val;
-			if (hvar_calc(o->hvarv[i], &name, &val, o->title)) {
+			if (hvar_calc(opts.hvarv[i], &name, &val, opts.title)) {
 				fprintf(stderr, "invalid argument given to --hvar\n");
 				exit(1);
 			}
@@ -422,15 +527,15 @@ Str html_head(Options *o, Arena *m, Arena *l) {
 		str_cat(&h, S("</style>\n"), m);
 	}
 
-	if (o->stylesheet.s) {
-		if (o->csslink) {
+	if (opts.stylesheet.s) {
+		if (opts.csslink) {
 			str_cat(&h, S("<link rel='stylesheet' href="), m);
-			str_cat_uri(&h, o->stylesheet, m);
+			str_cat_uri(&h, opts.stylesheet, m);
 			str_cat(&h, S(">"), m);
 		} else {
-			FILE *f = fopen(str_to_cstr(o->stylesheet, m), "r/o");
+			FILE *f = fopen(str_to_cstr(opts.stylesheet, m), "r/o");
 			if (!f) {
-				str_putf(o->stylesheet, stderr);
+				str_putf(opts.stylesheet, stderr);
 				fprintf(stderr, ": %s\n", strerror(errno));
 				exit(1);
 			}
@@ -455,16 +560,16 @@ int main(int argc, const char **argv) {
 	ARENA(scratch, 1 << 20)
 
 	Doc *doc = 0;
-	Options opts = { 0 };
 	int r;
 
 	ArgsState a = args_begin(argv);
 	Str param = { 0 };
 	opts.from_stdin = 1;
 
-	while ((r = arg_get(&a, "?slc:h:t:", &param,
+	while ((r = arg_get(&a, "?slic:h:t:", &param,
 					"help", '?',
 					"standalone", 's',
+					"inline", 'i',
 					":title", 't',
 					":css", 'c',
 					"link", 'l',
@@ -478,6 +583,9 @@ int main(int argc, const char **argv) {
 		case 's':
 			opts.standalone = 1;
 			break;
+		case 'i':
+			opts.inlinep = 1;
+			break;
 		case 'c':
 			opts.stylesheet = param;
 			break;
@@ -546,7 +654,7 @@ int main(int argc, const char **argv) {
 	}
 
 	if (opts.standalone) {
-		str_put(html_head(&opts, &perm, &scratch));
+		str_put(html_head(&perm, &scratch));
 		str_put(S("\n"));
 	}