about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--decode.c115
-rw-r--r--encode.c365
-rw-r--r--free.c5
-rw-r--r--gemtext.h16
-rw-r--r--strlcat.c55
-rw-r--r--strlcpy.c50
-rw-r--r--test.c109
8 files changed, 562 insertions, 158 deletions
diff --git a/Makefile b/Makefile
index c3eb3e4..e2a7cac 100644
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,15 @@
 CFLAGS = -Wall -Wextra -fPIC -g
 
-OBJ = free.o decode.o
+OBJ = free.o decode.o encode.o strlcpy.o strlcat.o
 
 all: libgemtext.a
 
 libgemtext.a: $(OBJ)
 	$(AR) rcs $@ $^
 
+test: libgemtext.a
+	$(CC) -g -o test libgemtext.a
+
 clean:
 	rm -f $(OBJ) libgemtext.a
 
diff --git a/decode.c b/decode.c
index 227d389..114ed91 100644
--- a/decode.c
+++ b/decode.c
@@ -1,5 +1,10 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "gemtext.h"
 
@@ -311,29 +316,6 @@ _case_pre(char **text)
 	return (struct gemtext *)ret;
 }
 
-static int
-_gemtext_ul_append(struct gemtext_ul *ul, char *text, int len)
-{
-	int nitems;
-	char **items;
-
-	items = ul->items;
-	nitems = ul->nitems;
-
-	items = realloc(items, sizeof(*items)*(nitems+1));
-	if (items == NULL)
-		return -1;
-	ul->items = items;
-
-	nitems++;
-	items[nitems-1] = strndup(text, len);
-	if (items[nitems-1] == NULL)
-		return -1;
-	ul->nitems = nitems;
-
-	return 0;
-}
-
 static struct gemtext *
 _case_ul(char **text)
 {
@@ -343,38 +325,34 @@ _case_ul(char **text)
 
 	ptr = *text;
 
-	/* should always be true, but just in case */
-	if (*ptr != '*')
-		return NULL;
-
 	ret = malloc(sizeof(*ret));
 	if (ret == NULL)
 		return NULL;
 	memset(ret, 0, sizeof(*ret));
 	ret->type = GEMTEXT_UL;
 
-	while (*ptr != '\0' && *ptr == '*') {
-		if (*(ptr+1) == ' ')
-			ptr += 2;
-		else
-			ptr++;
+	if (*(ptr+1) == ' ')
+		ptr += 2;
+	else
+		ptr++;
 
-		cpy = ptr;
-		while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
-			ptr++;
-		if (_gemtext_ul_append(ret, cpy, ptr-cpy) == -1) {
-			gemtext_free((struct gemtext *)ret);
-			return NULL;
-		}
+	cpy = ptr;
+	while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
+		ptr++;
 
-		if (*ptr == '\r') {
-			if (strncmp(ptr, "\r\n", 2) == 0)
-				ptr += 2;
-			else
-				ptr++;
-		} else if (*ptr == '\n') {
+	ret->text = strndup(cpy, ptr-cpy);
+	if (ret->text == NULL) {
+		gemtext_free((struct gemtext *)ret);
+		return NULL;
+	}
+
+	if (*ptr == '\r') {
+		if (strncmp(ptr, "\r\n", 2) == 0)
+			ptr += 2;
+		else
 			ptr++;
-		}
+	} else if (*ptr == '\n') {
+		ptr++;
 	}
 
 	*text = ptr;
@@ -541,3 +519,48 @@ gemtext_decode(char *text)
 
 	return ret;
 }
+
+struct gemtext **
+gemtext_decode_fd(int fd)
+{
+	struct gemtext **ret;
+	struct stat fst;
+	char *tbuf;
+
+	if (fstat(fd, &fst) == -1)
+		return NULL;
+	tbuf = malloc(fst.st_size+1);
+	if (tbuf == NULL)
+		return NULL;
+
+	if (read(fd, tbuf, fst.st_size) == -1) {
+		free(tbuf);
+		return NULL;
+	}
+	tbuf[fst.st_size-1] = '\0';
+
+	ret = gemtext_decode(tbuf);
+	free(tbuf);
+	if (ret == NULL)
+		return NULL;
+
+	return ret;
+}
+
+struct gemtext **
+gemtext_decode_file(const char *path)
+{
+	struct gemtext **ret;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return NULL;
+
+	ret = gemtext_decode_fd(fd);
+	close(fd);
+	if (ret == NULL)
+		return NULL;
+
+	return ret;
+}
diff --git a/encode.c b/encode.c
new file mode 100644
index 0000000..efe8aaa
--- /dev/null
+++ b/encode.c
@@ -0,0 +1,365 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "gemtext.h"
+
+static char *
+_case_text(struct gemtext *t)
+{
+	struct gemtext_text *text;
+
+	text = (struct gemtext_text *)t;
+
+	return strdup(text->text);
+}
+
+static char *
+_case_link(struct gemtext *t)
+{
+	struct gemtext_link *link;
+	char *ret;
+	int retlen;
+
+	retlen = 0;
+	link = (struct gemtext_link *)t;
+
+	retlen += strlen(link->link);
+	if (link->name)
+		retlen += strlen(link->name);
+	retlen += 3; /* three characters for "=> " */
+	retlen += 1; /* \0 */
+	if (link->name)
+		retlen += 1; /* additional whitespace separator */
+
+	ret = malloc(retlen+1);
+	if (ret == NULL)
+		return NULL;
+
+	strlcpy(ret, "=> ", retlen);
+	strlcat(ret, link->link, retlen);
+	if (link->name) {
+		strlcat(ret, " ", retlen);
+		strlcat(ret, link->name, retlen);
+	}
+
+	return ret;
+}
+
+static char *
+_case_pre(struct gemtext *t)
+{
+	struct gemtext_pre *pre;
+
+	pre = (struct gemtext_pre *)t;
+
+	return strdup(pre->text);
+}
+
+static char *
+_case_h1(struct gemtext *t)
+{
+	struct gemtext_h1 *h1;
+	char *ret;
+	int retlen;
+
+	retlen = 0;
+	h1 = (struct gemtext_h1 *)t;
+
+	retlen += strlen(h1->text);
+	retlen += 2; /* two characters for "# " */
+	retlen += 1; /* \0 */
+
+	ret = malloc(retlen+1);
+	if (ret == NULL)
+		return NULL;
+
+	strlcpy(ret, "# ", retlen);
+	strlcat(ret, h1->text, retlen);
+
+	return ret;
+}
+
+static char *
+_case_h2(struct gemtext *t)
+{
+	struct gemtext_h2 *h2;
+	char *ret;
+	int retlen;
+
+	retlen = 0;
+	h2 = (struct gemtext_h2 *)t;
+
+	retlen += strlen(h2->text);
+	retlen += 3; /* three characters for "## " */
+	retlen += 1; /* \0 */
+
+	ret = malloc(retlen+1);
+	if (ret == NULL)
+		return NULL;
+
+	strlcpy(ret, "## ", retlen);
+	strlcat(ret, h2->text, retlen);
+
+	return ret;
+}
+
+static char *
+_case_h3(struct gemtext *t)
+{
+	struct gemtext_h3 *h3;
+	char *ret;
+	int retlen;
+
+	retlen = 0;
+	h3 = (struct gemtext_h3 *)t;
+
+	retlen += strlen(h3->text);
+	retlen += 4; /* four characters for "### " */
+	retlen += 1; /* \0 */
+
+	ret = malloc(retlen+1);
+	if (ret == NULL)
+		return NULL;
+
+	strlcpy(ret, "### ", retlen);
+	strlcat(ret, h3->text, retlen);
+
+	return ret;
+}
+
+
+static char *
+_case_ul(struct gemtext *t)
+{
+	struct gemtext_ul *ul;
+	char *ret;
+	int retlen;
+
+	retlen = 0;
+	ul = (struct gemtext_ul *)t;
+
+	retlen += strlen(ul->text);
+	retlen += 2; /* two characters for "* " */
+	retlen += 1; /* \0 */
+
+	ret = malloc(retlen+1);
+	if (ret == NULL)
+		return NULL;
+
+	strlcpy(ret, "* ", retlen);
+	strlcat(ret, ul->text, retlen);
+
+	return ret;
+}
+
+static char *
+_case_qt(struct gemtext *t)
+{
+	struct gemtext_qt *qt;
+	char *ret;
+	int retlen;
+
+	retlen = 0;
+	qt = (struct gemtext_qt *)t;
+
+	retlen += strlen(qt->text);
+	retlen += 2; /* two characters for *> " */
+	retlen += 1; /* \0 */
+
+	ret = malloc(retlen+1);
+	if (ret == NULL)
+		return NULL;
+
+	strlcpy(ret, "> ", retlen);
+	strlcat(ret, qt->text, retlen);
+
+	return ret;
+}
+
+static char *
+_line_append(char *dst, const char *src, int *len)
+{
+	int rlen;
+	char *ret;
+
+	rlen = *len;
+	rlen += strlen(src);
+	rlen += 1; /* \n */
+
+	ret = realloc(dst, rlen+1);
+	if (ret == NULL)
+		return NULL;
+
+	strlcat(ret, src, rlen+1);
+	strlcat(ret, "\n", rlen+1);
+
+	*len = rlen;
+	return ret;
+}
+
+int
+gemtext_encode(struct gemtext **list, char **text, int *len)
+{
+	int i;
+	int rtextlen;
+	char *rtext;
+	char *retbuf;
+
+	rtextlen = 0;
+	rtext = NULL;
+
+	for (i = 0; list[i] != NULL; i++) {
+		switch (list[i]->type) {
+		case GEMTEXT_TEXT:
+			retbuf = _case_text(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		case GEMTEXT_LINK:
+			retbuf = _case_link(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		case GEMTEXT_PRE:
+			retbuf = _case_pre(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		case GEMTEXT_H1:
+			retbuf = _case_h1(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		case GEMTEXT_H2:
+			retbuf = _case_h2(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		case GEMTEXT_H3:
+			retbuf = _case_h3(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		case GEMTEXT_UL:
+			retbuf = _case_ul(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		case GEMTEXT_QT:
+			retbuf = _case_qt(list[i]);
+			if (retbuf == NULL) {
+				free(rtext);
+				return -1;
+			}
+			rtext = _line_append(rtext, retbuf, &rtextlen);
+			free(retbuf);
+			if (rtext == NULL) {
+				free(rtext);
+				return -1;
+			}
+			break;
+		}
+	}
+
+	*text = rtext;
+	*len = rtextlen;
+
+	return 0;
+}
+
+
+int
+gemtext_encode_fd(struct gemtext **list, int fd)
+{
+	char *text;
+	int len;
+	int ecode;
+
+	if (gemtext_encode(list, &text, &len) == -1)
+		return -1;
+
+	ecode = write(fd, text, len);
+	if (ecode == -1)
+		return -1;
+
+	return 0;
+}
+
+int
+gemtext_encode_file(struct gemtext **list, const char *path)
+{
+	int fd;
+	int ecode;
+
+	fd = open(path, O_WRONLY);
+	if (fd == -1)
+		return -1;
+
+	ecode = gemtext_encode_fd(list, fd);
+	close(fd);
+	if (ecode == -1)
+		return -1;
+
+	return 0;
+}
diff --git a/free.c b/free.c
index f68d1be..8767066 100644
--- a/free.c
+++ b/free.c
@@ -42,10 +42,7 @@ _free_h3(struct gemtext_h3 *t)
 static void
 _free_ul(struct gemtext_ul *t)
 {
-	int i;
-	for (i = 0; i < t->nitems; i++)
-		free(t->items[i]);
-	free(t->items);
+	free(t->text);
 }
 
 static void
diff --git a/gemtext.h b/gemtext.h
index f2bb766..1b915b8 100644
--- a/gemtext.h
+++ b/gemtext.h
@@ -44,8 +44,7 @@ struct gemtext_h3 {
 
 struct gemtext_ul {
 	int type;
-	int nitems;
-	char **items;
+	char *text;
 };
 
 struct gemtext_qt {
@@ -53,8 +52,21 @@ struct gemtext_qt {
 	char *text;
 };
 
+/* strlcpy.c/strlcat.c */
+#undef strlcpy
+#undef strlcat
+size_t strlcpy(char *, const char *, size_t);
+size_t strlcat(char *, const char *, size_t);
+
 /* decode.c */
 struct gemtext **gemtext_decode(char *);
+struct gemtext **gemtext_decode_fd(int);
+struct gemtext **gemtext_decode_file(const char *);
+
+/* encode.c */
+int gemtext_encode(struct gemtext **, char **, int *);
+int gemtext_encode_fd(struct gemtext **, int);
+int gemtext_encode_file(struct gemtext **, const char *);
 
 /* free.c */
 void gemtext_free(struct gemtext *);
diff --git a/strlcat.c b/strlcat.c
new file mode 100644
index 0000000..c94e90d
--- /dev/null
+++ b/strlcat.c
@@ -0,0 +1,55 @@
+/*	$OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size dsize (unlike strncat, dsize is the
+ * full size of dst, not space left).  At most dsize-1 characters
+ * will be copied.  Always NUL terminates (unless dsize <= strlen(dst)).
+ * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
+ * If retval >= dsize, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t dsize)
+{
+	const char *odst = dst;
+	const char *osrc = src;
+	size_t n = dsize;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end. */
+	while (n-- != 0 && *dst != '\0')
+		dst++;
+	dlen = dst - odst;
+	n = dsize - dlen;
+
+	if (n-- == 0)
+		return(dlen + strlen(src));
+	while (*src != '\0') {
+		if (n != 0) {
+			*dst++ = *src;
+			n--;
+		}
+		src++;
+	}
+	*dst = '\0';
+
+	return(dlen + (src - osrc));	/* count does not include NUL */
+}
diff --git a/strlcpy.c b/strlcpy.c
new file mode 100644
index 0000000..2fa498c
--- /dev/null
+++ b/strlcpy.c
@@ -0,0 +1,50 @@
+/*	$OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy string src to buffer dst of size dsize.  At most dsize-1
+ * chars will be copied.  Always NUL terminates (unless dsize == 0).
+ * Returns strlen(src); if retval >= dsize, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t dsize)
+{
+	const char *osrc = src;
+	size_t nleft = dsize;
+
+	/* Copy as many bytes as will fit. */
+	if (nleft != 0) {
+		while (--nleft != 0) {
+			if ((*dst++ = *src++) == '\0')
+				break;
+		}
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src. */
+	if (nleft == 0) {
+		if (dsize != 0)
+			*dst = '\0';		/* NUL-terminate dst */
+		while (*src++)
+			;
+	}
+
+	return(src - osrc - 1);	/* count does not include NUL */
+}
diff --git a/test.c b/test.c
index 066453a..411806b 100644
--- a/test.c
+++ b/test.c
@@ -1,120 +1,19 @@
+#include <stdlib.h>
 #include <stdio.h>
 
 #include "gemtext.h"
 
-static void
-_do_text(struct gemtext_text *t)
-{
-	printf("TEXT: %s\n", t->text);
-}
-
-static void
-_do_link(struct gemtext_link *t)
-{
-	printf("LINK: (name: %s) (link: %s)\n", t->name, t->link);
-}
-
-static void
-_do_pre(struct gemtext_pre *t)
-{
-	printf("PRE:\n%s\n", t->text);
-}
-
-static void
-_do_h1(struct gemtext_h1 *t)
-{
-	printf("H1: %s\n", t->text);
-}
-
-static void
-_do_h2(struct gemtext_h2 *t)
-{
-	printf("H2: %s\n", t->text);
-}
-
-static void
-_do_h3(struct gemtext_h3 *t)
-{
-	printf("H3: %s\n", t->text);
-}
-
-static void
-_do_ul(struct gemtext_ul *t)
-{
-	int i;
-
-	for (i = 0; i < t->nitems; i++)
-		printf("UL: %d: %s\n", i+1, t->items[i]);
-}
-
-static void
-_do_qt(struct gemtext_qt *t)
-{
-	printf("QT: %s\n", t->text);
-}
-
-static void
-_printer(struct gemtext **list)
-{
-	int i;
-
-	for (i = 0; list[i] != NULL; i++) {
-		switch (list[i]->type) {
-		case GEMTEXT_TEXT:
-			_do_text((struct gemtext_text *)list[i]);
-			break;
-		case GEMTEXT_LINK:
-			_do_link((struct gemtext_link *)list[i]);
-			break;
-		case GEMTEXT_PRE:
-			_do_pre((struct gemtext_pre *)list[i]);
-			break;
-		case GEMTEXT_H1:
-			_do_h1((struct gemtext_h1 *)list[i]);
-			break;
-		case GEMTEXT_H2:
-			_do_h2((struct gemtext_h2 *)list[i]);
-			break;
-		case GEMTEXT_H3:
-			_do_h3((struct gemtext_h3 *)list[i]);
-			break;
-		case GEMTEXT_UL:
-			_do_ul((struct gemtext_ul *)list[i]);
-			break;
-		case GEMTEXT_QT:
-			_do_qt((struct gemtext_qt *)list[i]);
-			break;
-		}
-	}
-}
-
-char *_example =
-"does it continue?\n"
-"```\n"
-"SO much text\n"
-"I know right```\n"
-"`\n"
-"``\n"
-" ```\n"
-"```\n"
-"*hello there\n"
-"* this\n"
-"*is\n"
-"* an amazing list\n"
-">this is a greentext\n"
-"> this is a \"quote\"\n";
-
-
 int
 main(void)
 {
 	struct gemtext **lst;
 
-	lst = gemtext_decode(_example);
+	lst = gemtext_decode_file("/dev/stdin");
 	if (lst == NULL)
 		return 1;
 
-	_printer(lst);
+	if (gemtext_encode_file(lst, "/dev/stdout") == -1)
+		return 1;
 
 	gemtext_list_free(lst);
 	return 0;