about summary refs log blame commit diff stats
path: root/decode.c
blob: a0fcac215272a0fd3d1ac4bacbf14d1ae146f488 (plain) (tree)
1
2
3
4
5
6
7



                      

                   
                   























                                                            
                                                    

































                                                 
                                 
                                                             
                      
                  
                                                                                           


                                          


                                                  

































                                                                    

                                                             







                                                            
                                                    






























                                                 


                                                             






                                                            
                                                    






























                                                 


                                                             






                                                            
                                                    






































































                                                                  
                                                                            












                                                    
                                                            






















                                                                    








                               





                                     


                                                 
 


                                                            
 

                                          
                                                    






                                                 
                              

                                  




















                                     
              





                                                            
                                                    















                                                 






















                                           

                          














                                      
                                       




                                                





























                                     







                                




                                               
                                                        


                                               
                 
                             

         











                                                                   

                   

                 
                              









                                     
                                       




                                                
 
                                        







                            
                                          







                                  
                                         





                            
#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "gemtext.h"

static struct gemtext *
_case_default(char **text)
{
	struct gemtext_text *ret;
	char *ptr;
	char *cpy;

	ptr = *text;
	cpy = ptr;

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_TEXT;

	while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
		ptr++;

	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;
	return (struct gemtext *)ret;
}

static struct gemtext *
_case_link(char **text)
{
	struct gemtext_link *ret;
	char *ptr;
	char *cpy;

	ptr = *text;

	if (*(ptr+1) != '>')
		return _case_default(text);

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_LINK;

	ptr += 2; /* skip "=>" */
	while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t'))
		ptr++;
	cpy = ptr;
	while (*ptr != '\r' && *ptr != '\n' && *ptr != ' ' && *ptr != '\t' && *ptr != '\0')
		ptr++;
	ret->link = strndup(cpy, ptr-cpy);

	if (*ptr == ' ' || *ptr == '\t') {
		while(*ptr == ' ' || *ptr == '\t')
			ptr++;
		cpy = ptr;
		while (*ptr != '\r' && *ptr != '\n' && *ptr != '\0')
			ptr++;
		ret->name = strndup(cpy, ptr-cpy);
	}

	if (*ptr == '\r') {
		if (strncmp(ptr, "\r\n", 2) == 0)
			ptr += 2;
		else
			ptr++;
	} else if (*ptr == '\n') {
		ptr++;
	}

	*text = ptr;
	return (struct gemtext *)ret;
}

static struct gemtext *
_case_h1(char **text)
{
	struct gemtext_h1 *ret;
	char *ptr;
	char *cpy;

	ptr = *text;

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_H1;

	ptr++;
	while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t'))
		ptr++;

	cpy = ptr;
	while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
		ptr++;

	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;
	return (struct gemtext *)ret;
}

static struct gemtext *
_case_h2(char **text)
{
	struct gemtext_h2 *ret;
	char *ptr;
	char *cpy;

	ptr = *text;

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_H2;

	ptr += 2;
	while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t'))
		ptr++;

	cpy = ptr;
	while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
		ptr++;

	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;
	return (struct gemtext *)ret;
}

static struct gemtext *
_case_h3(char **text)
{
	struct gemtext_h3 *ret;
	char *ptr;
	char *cpy;

	ptr = *text;

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_H3;

	ptr += 3;
	while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t'))
		ptr++;

	cpy = ptr;
	while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
		ptr++;

	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;
	return (struct gemtext *)ret;
}

static struct gemtext *
_case_h(char **text)
{
	char *ptr;

	ptr = *text;

	if (*(ptr+1) == '#') {
		if (*(ptr+2) == '#')
			return _case_h3(text);
		else
			return _case_h2(text);
	} else {
		return _case_h1(text);
	}

	/* NOT REACHED */
	return NULL;
}

static struct gemtext *
_case_pre(char **text)
{
	struct gemtext_pre *ret;
	char *ptr;
	char *cpy;

	ptr = *text;

	if (strncmp(ptr, "```", 3) != 0)
		return _case_default(text);

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_PRE;

	ptr += 3;
	if (*ptr == '\r') {
		if (strncmp(ptr, "\r\n", 2) == 0)
			ptr += 2;
		else
			ptr++;
	} else if (*ptr == '\n') {
		ptr++;
	}

	cpy = ptr;
	while (*ptr != '\0') {
		if (*ptr == '\n' || *ptr == '\r') {
			if (strncmp(ptr+1, "```", 3) == 0) {
				ptr++;
				ret->text = strndup(cpy, ptr-cpy);
				if (ret->text == NULL) {
					gemtext_free((struct gemtext *)ret);
					return NULL;
				}
				break;
			}
		}
		ptr++;
	}

	/* true if closing "```" was never found
	   in while loop above */
	if (*ptr == '\0') {
		ret->text = strndup(cpy, ptr-cpy);
		if (ret->text == NULL) {
			gemtext_free((struct gemtext *)ret);
			return NULL;
		}
	}

	/* should be always true if above statement is false */
	if (*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') {
		ptr++;
	}

	*text = ptr;
	return (struct gemtext *)ret;
}

static struct gemtext *
_case_ul(char **text)
{
	struct gemtext_ul *ret;
	char *ptr;
	char *cpy;

	ptr = *text;

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_UL;

	if (*(ptr+1) != ' ')
		return _case_default(text);
	ptr += 2; /* skip whitespace after '*' */

	cpy = ptr;
	while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
		ptr++;

	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;
	return (struct gemtext *)ret;
}

static struct gemtext *
_case_qt(char **text)
{
	struct gemtext_qt *ret;
	char *ptr;
	char *cpy;

	ptr = *text;

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		return NULL;
	memset(ret, 0, sizeof(*ret));
	ret->type = GEMTEXT_QT;

	ptr++;
	cpy = ptr;
	while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
		ptr++;

	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;
	return (struct gemtext *)ret;
}

static struct gemtext *
_gemtext_decode(char **text)
{
	switch (**text) {
	case '=':
		return _case_link(text);
	case '#':
		return _case_h(text);
	case '`':
		return _case_pre(text);
	case '*':
		return _case_ul(text);
	case '>':
		return _case_qt(text);
	default:
		return _case_default(text);
	}

	/* NOT REACHED */
	return NULL;
}

struct gemtext *
gemtext_decode(char *text)
{
	return _gemtext_decode(&text);
}

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;
	memset(tbuf, 0, fst.st_size+1);

	if (read(fd, tbuf, fst.st_size) == -1) {
		free(tbuf);
		return NULL;
	}

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

struct gemtext **
gemtext_list_decode(char *text)
{
	struct gemtext **ret;
	struct gemtext **append;
	struct gemtext *item;
	char *ptr;

	ret = NULL;
	ptr = text;
	while (*ptr) {
		item = _gemtext_decode(&ptr);
		if (item == NULL) {
			gemtext_list_free(ret);
			return NULL;
		}
		append = gemtext_list_append(ret, item);
		if (append == NULL) {
			gemtext_list_free(ret);
			return NULL;
		}
		ret = append;
	}

	/*
	 * if this is reached, it means there was no text to parse
	 * therefore NULL would be returned, but we don't want that
	 * because it is the error indicator, so we'll create an
	 * empty list
	 */
	if (ret == NULL) {
		ret = malloc(sizeof(*ret));
		if (ret == NULL)
			return NULL;
		ret[0] = NULL;
	}
	return ret;
}

struct gemtext **
gemtext_list_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;
	memset(tbuf, 0, fst.st_size+1);

	if (read(fd, tbuf, fst.st_size) == -1) {
		free(tbuf);
		return NULL;
	}

	ret = gemtext_list_decode(tbuf);
	free(tbuf);
	if (ret == NULL)
		return NULL;

	return ret;
}

struct gemtext **
gemtext_list_decode_file(const char *path)
{
	struct gemtext **ret;
	int fd;

	fd = open(path, O_RDONLY);
	if (fd == -1)
		return NULL;

	ret = gemtext_list_decode_fd(fd);
	close(fd);
	if (ret == NULL)
		return NULL;

	return ret;
}