diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | decode.c | 115 | ||||
-rw-r--r-- | encode.c | 365 | ||||
-rw-r--r-- | free.c | 5 | ||||
-rw-r--r-- | gemtext.h | 16 | ||||
-rw-r--r-- | strlcat.c | 55 | ||||
-rw-r--r-- | strlcpy.c | 50 | ||||
-rw-r--r-- | test.c | 109 |
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; |