about summary refs log blame commit diff stats
path: root/encode.c
blob: 75afb1823b45103522c3bf9d7b991d731a1ffdf0 (plain) (tree)
























                                        
                      











                                                                  
                             
















                                                 
                  
                      
 
                   

                                      













                                                                      






                              
                      







                                                  
                             













                                       
                      







                                                     
                             













                                       
                      







                                                     
                             














                                       
                      







                                                  
                             













                                       
                      




                                    
                            

                             
                             


                            
                                  





                                       
                                                     
 
                    








                                   



                                                             








                                   
                                                              





                                         

                                             




                                         

                                             




                                        

                                             




                                       

                                             




                                       

                                             




                                       

                                             




                                       

                                             




                                       

                                             










                                               
                   































                                                           
                                                                    

              

                         

                     

                     

                      

                                           
                                                                         
                                     




                                                                  
                                     
                                  
                 
                                

         

                         





                 
                                                     

                   
                   

                  
                                                         


                                     
                   






                          
                                                                 



                  
                                                  

                          

                                                                            
 
                                                 





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