summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc.c5
-rw-r--r--doc.h1
-rw-r--r--parse.c112
3 files changed, 103 insertions, 15 deletions
diff --git a/doc.c b/doc.c
index 3f63ba8..89ff756 100644
--- a/doc.c
+++ b/doc.c
@@ -59,6 +59,11 @@ unsigned short doc_add_link(struct doc *d, const char *url) {
 	return d->linkc++;
 }
 
+void doc_set_link(struct doc *d, unsigned short lnk) {
+	struct doc_line *l = doc_line_at(d, d->latest);
+	l->link = lnk;
+}
+
 /* line navigation */
 
 struct doc_line *doc_line_at(struct doc *d, size_t ofs) {
diff --git a/doc.h b/doc.h
index ea9a521..08af700 100644
--- a/doc.h
+++ b/doc.h
@@ -35,6 +35,7 @@ struct doc_line *doc_line_at(struct doc *d, size_t ofs);
 int doc_line_prev(struct doc *d, size_t *ofs);
 int doc_line_next(struct doc *d, size_t *ofs);
 
+void doc_set_link(struct doc *d, unsigned short lnk);
 unsigned short doc_add_link(struct doc *d, const char *url);
 
 #endif
diff --git a/parse.c b/parse.c
index 22e7edf..601c6b1 100644
--- a/parse.c
+++ b/parse.c
@@ -1,25 +1,107 @@
 #include "parse.h"
 #include "err.h"
 
+int parse_plain(struct doc *d, const buf_t *b) {
+	doc_init(d);
+	for (size_t i = 0; i < b->sz; i++) {
+		char c = b->buf[i];
+		if (c == '\n') {
+			doc_new_line(d);
+		} else {
+			doc_add_textn(d, &c, 1);
+		}
+	}
+	return 0;
+}
+
+struct str_slice {
+	const char *s;
+	size_t n;
+};
+
+static struct str_slice gmbit(size_t *i, const char *s, size_t n) {
+	struct str_slice ss = {
+		&s[*i],
+		0
+	};
+	while (*i < n && s[*i] != '\t') {
+		*i += 1;
+		ss.n++;
+	}
+	*i += 1;
+	return ss;
+}
+
+size_t scatss(char *buf, size_t i, size_t n, struct str_slice ss) {
+	size_t si = 0;
+	while (i < n && si < ss.n) {
+		buf[i++] = ss.s[si++];
+	}
+	return i;
+}
+
+int parse_gophermap_line(struct doc *d, const char *s, size_t n) {
+	char url[512] = "gopher://";
+	size_t urln = 9;
+	struct {
+		char item_type;
+		struct str_slice dstr;
+		struct str_slice sel;
+		struct str_slice host;
+		struct str_slice port;
+	} bits;
+	size_t i = 0;
+	bits.item_type = s[i++];
+	bits.dstr = gmbit(&i, s, n);
+	bits.sel = gmbit(&i, s, n);
+	bits.host = gmbit(&i, s, n);
+	bits.port = gmbit(&i, s, n);
+	switch (bits.item_type) {
+	case '.':
+		if (n == 1) return 1;
+	default:
+		urln = scatss(url, urln, sizeof url, bits.host);
+		if (urln < sizeof url) url[urln++] = ':';
+		urln = scatss(url, urln, sizeof url, bits.port);
+		if (urln < sizeof url) url[urln++] = '/';
+		if (urln < sizeof url) url[urln++] = bits.item_type;
+		urln = scatss(url, urln, sizeof url, bits.sel);
+		url[urln] = 0;
+		doc_set_link(d, doc_add_link(d, url));
+	case 'i':
+		doc_add_textn(d, bits.dstr.s, bits.dstr.n);
+		doc_new_line(d);
+		break;
+	}
+	return 0;
+}
+
+int parse_gophermap(struct doc *d, const buf_t *b) {
+	doc_init(d);
+	size_t ln_start = 0;
+	for (size_t i = 0; i < b->sz; i++) {
+		if (b->buf[i] == '\r') continue;
+		if (b->buf[i] == '\n') {
+			char *ln_str = &b->buf[ln_start];
+			size_t ln_len = i - ln_start;
+			if (i > 0 && b->buf[i - 1] == '\r') ln_len--;
+			if (parse_gophermap_line(d, ln_str, ln_len)) {
+				break;
+			}
+			ln_start = i + 1;
+		}
+	}
+	return 0;
+}
+
 int parse_doc(enum doc_type type, struct doc *d, const buf_t *b) {
 	switch (type) {
 	case DOC_PLAIN:
-		doc_init(d);
-		for (size_t i = 0; i < b->sz; i++) {
-			char c = b->buf[i];
-			if (c == '\n') {
-				doc_new_line(d);
-			} else {
-				doc_add_textn(d, &c, 1);
-			}
-		}
-		goto ok;
+		return parse_plain(d, b);
+	case DOC_GOPHERMAP:
+		return parse_gophermap(d, b);
 	default:
 		perr("unsupported doctype");
-		goto err;
+		return -1;
 	}
-ok:
-	return 0;
-err:
-	return -1;
 }