#include #include #include #include #include #include #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; size_t 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); 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; char *ret; size_t retlen; retlen = 0; pre = (struct gemtext_pre *)t; retlen += strlen(pre->text); retlen += 6; /* "```" at the beginning and "```" at the end */ retlen += 1; /* \n after opening "```" */ retlen += 1; /* \0 */ ret = malloc(retlen); if (ret == NULL) return NULL; strlcpy(ret, "```\n", retlen); strlcat(ret, pre->text, retlen); strlcat(ret, "```", retlen); return ret; } static char * _case_h1(struct gemtext *t) { struct gemtext_h1 *h1; char *ret; size_t retlen; retlen = 0; h1 = (struct gemtext_h1 *)t; retlen += strlen(h1->text); retlen += 2; /* two characters for "# " */ retlen += 1; /* \0 */ ret = malloc(retlen); 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; size_t retlen; retlen = 0; h2 = (struct gemtext_h2 *)t; retlen += strlen(h2->text); retlen += 3; /* three characters for "## " */ retlen += 1; /* \0 */ ret = malloc(retlen); 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; size_t retlen; retlen = 0; h3 = (struct gemtext_h3 *)t; retlen += strlen(h3->text); retlen += 4; /* four characters for "### " */ retlen += 1; /* \0 */ ret = malloc(retlen); 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; size_t retlen; retlen = 0; ul = (struct gemtext_ul *)t; retlen += strlen(ul->text); retlen += 2; /* two characters for "* " */ retlen += 1; /* \0 */ ret = malloc(retlen); 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; size_t retlen; retlen = 0; qt = (struct gemtext_qt *)t; retlen += strlen(qt->text); retlen += 1; /* > */ retlen += 1; /* \0 */ ret = malloc(retlen); if (ret == NULL) return NULL; strlcpy(ret, ">", retlen); strlcat(ret, qt->text, retlen); return ret; } static char * _line_append(char *dst, const char *src, size_t *len) { size_t rlen; char *ret; rlen = *len; rlen += strlen(src); rlen += 1; /* \n */ ret = realloc(dst, rlen+1); if (ret == NULL) return NULL; if (dst == NULL) /* this ensures that the first byte strlcat() * encounters is a null byte */ memset(ret, 0, rlen+1); strlcat(ret, src, rlen+1); strlcat(ret, "\n", rlen+1); *len = rlen; return ret; } int gemtext_encode(struct gemtext *line, char **text, size_t *len) { switch (line->type) { case GEMTEXT_TEXT: *text = _case_text(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; case GEMTEXT_LINK: *text = _case_link(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; case GEMTEXT_PRE: *text = _case_pre(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; case GEMTEXT_H1: *text = _case_h1(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; case GEMTEXT_H2: *text = _case_h2(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; case GEMTEXT_H3: *text = _case_h3(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; case GEMTEXT_UL: *text = _case_ul(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; case GEMTEXT_QT: *text = _case_qt(line); if (*text == NULL) return -1; if (len) *len = strlen(*text); return 0; } /* NOT REACHED */ return -1; } int gemtext_encode_fd(struct gemtext *line, int fd) { char *text; size_t len; int ecode; if (gemtext_encode(line, &text, &len) == -1) return -1; ecode = write(fd, text, len); free(text); if (ecode == -1) return -1; return 0; } int gemtext_encode_file(struct gemtext *line, const char *path) { int fd; int ecode; fd = open(path, O_WRONLY); if (fd == -1) return -1; ecode = gemtext_encode_fd(line, fd); close(fd); if (ecode == -1) return -1; return 0; } int gemtext_list_encode(struct gemtext **list, char **text, size_t *len) { int i; size_t encbuflen; size_t retbuflen; char *encbuf; char *appbuf; char *retbuf; retbuflen = 0; retbuf = NULL; for (i = 0; list[i] != NULL; i++) { if (gemtext_encode(list[i], &encbuf, &encbuflen) == -1) { free(retbuf); return -1; } appbuf = _line_append(retbuf, encbuf, &retbuflen); free(encbuf); if (appbuf == NULL) { free(retbuf); return -1; } retbuf = appbuf; } *text = retbuf; *len = retbuflen; return 0; } int gemtext_list_encode_fd(struct gemtext **list, int fd) { char *text; size_t len; int ecode; if (gemtext_list_encode(list, &text, &len) == -1) return -1; ecode = write(fd, text, len); free(text); if (ecode == -1) return -1; return 0; } int gemtext_list_encode_file(struct gemtext **list, const char *path) { int fd; int ecode; fd = open(path, O_WRONLY|O_TRUNC|O_CREAT); if (fd == -1) return -1; /* file does not have to be owned by user, so errors are expected */ fchmod(fd, 0644); ecode = gemtext_list_encode_fd(list, fd); close(fd); if (ecode == -1) return -1; return 0; }