#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;
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;
}