summary refs log blame commit diff stats
path: root/gemlog.c
blob: 650a28b1e5534090e15c0b4b312aa23ad2cce9cf (plain) (tree)































































































































































































































































































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

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

#include <gemtext.h>

#include "gemlog.h"

static struct gemlog_entry **gemlog_readdir_root(struct gemlog_entry **, struct tm, const char *);
static struct gemlog_entry **gemlog_readdir_year(struct gemlog_entry **, struct tm, const char *);
static struct gemlog_entry **gemlog_readdir_month(struct gemlog_entry **, struct tm, const char *);
static struct gemlog_entry **gemlog_readdir_day(struct gemlog_entry **, struct tm, const char *);
static struct gemlog_entry **gemlog_readdir_num(struct gemlog_entry **, struct tm, const char *);

static struct gemlog_entry **
gemlog_entry_list_append(struct gemlog_entry **list, struct gemlog_entry *item)
{
	int nitems;
	struct gemlog_entry **ret;

	if (list == NULL) {
		nitems = 2;
	} else {
		for (nitems = 0; list[nitems] != NULL; nitems++); /* watch out */
		nitems += 2; /* one for new item, one for terminating NULL */
	}

	ret = realloc(list, nitems*sizeof(*list));
	if (ret == NULL)
		return NULL;

	ret[nitems-1] = NULL;
	ret[nitems-2] = item;

	return ret;
}

static struct gemlog_entry **
gemlog_readdir_root(struct gemlog_entry **feed, struct tm timestamp, const char *path)
{
	DIR *directory;
	struct gemlog_entry **ret;
	struct dirent *dirlist;
	char pathbuf[PATH_MAX+1];

	directory = opendir(path);
	if (directory == NULL)
		return NULL;

	dirlist = readdir(directory);
	while (dirlist != NULL) {
		if (strcmp(dirlist->d_name, ".") == 0 || strcmp(dirlist->d_name, "..") == 0) {
			dirlist = readdir(directory);
			continue;
		}
		/* because tm_year adds 1900 */
		timestamp.tm_year = ((int)strtol(dirlist->d_name, NULL, 10)) - 1900;
		strlcpy(pathbuf, path, sizeof(pathbuf));
		strlcat(pathbuf, "/", sizeof(pathbuf));
		strlcat(pathbuf, dirlist->d_name, sizeof(pathbuf));
		ret = gemlog_readdir_year(feed, timestamp, pathbuf);
		if (ret == NULL) {
			gemlog_entry_list_free(feed);
			closedir(directory);
			return NULL;
		}
		feed = ret;
		dirlist = readdir(directory);
	}

	closedir(directory);
	return ret;
}

static struct gemlog_entry **
gemlog_readdir_year(struct gemlog_entry **feed, struct tm timestamp, const char *path)
{
	DIR *directory;
	struct gemlog_entry **ret;
	struct dirent *dirlist;
	char pathbuf[PATH_MAX+1];

	directory = opendir(path);
	if (directory == NULL)
		return NULL;

	dirlist = readdir(directory);
	while (dirlist != NULL) {
		if (strcmp(dirlist->d_name, ".") == 0 || strcmp(dirlist->d_name, "..") == 0) {
			dirlist = readdir(directory);
			continue;
		}
		timestamp.tm_mon = (int)strtol(dirlist->d_name, NULL, 10);
		timestamp.tm_mon--; /* Month is range (0-11) */
		strlcpy(pathbuf, path, sizeof(pathbuf));
		strlcat(pathbuf, "/", sizeof(pathbuf));
		strlcat(pathbuf, dirlist->d_name, sizeof(pathbuf));
		ret = gemlog_readdir_month(feed, timestamp, pathbuf);
		if (ret == NULL) {
			closedir(directory);
			return NULL;
		}
		feed = ret;
		dirlist = readdir(directory);
	}

	closedir(directory);
	return ret;
}

static struct gemlog_entry **
gemlog_readdir_month(struct gemlog_entry **feed, struct tm timestamp, const char *path)
{
	DIR *directory;
	struct gemlog_entry **ret;
	struct dirent *dirlist;
	char pathbuf[PATH_MAX+1];

	directory = opendir(path);
	if (directory == NULL)
		return NULL;

	dirlist = readdir(directory);
	while (dirlist != NULL) {
		if (strcmp(dirlist->d_name, ".") == 0 || strcmp(dirlist->d_name, "..") == 0) {
			dirlist = readdir(directory);
			continue;
		}
		timestamp.tm_mday = (int)strtol(dirlist->d_name, NULL, 10);
		strlcpy(pathbuf, path, sizeof(pathbuf));
		strlcat(pathbuf, "/", sizeof(pathbuf));
		strlcat(pathbuf, dirlist->d_name, sizeof(pathbuf));
		ret = gemlog_readdir_day(feed, timestamp, pathbuf);
		if (ret == NULL) {
			closedir(directory);
			return NULL;
		}
		feed = ret;
		dirlist = readdir(directory);
	}

	closedir(directory);
	return ret;
}

static struct gemlog_entry **
gemlog_readdir_day(struct gemlog_entry **feed, struct tm timestamp, const char *path)
{
	DIR *directory;
	struct gemlog_entry **ret;
	struct dirent *dirlist;
	char pathbuf[PATH_MAX+1];

	directory = opendir(path);
	if (directory == NULL)
		return NULL;

	dirlist = readdir(directory);
	while (dirlist != NULL) {
		if (strcmp(dirlist->d_name, ".") == 0 || strcmp(dirlist->d_name, "..") == 0) {
			dirlist = readdir(directory);
			continue;
		}
		/* hour is set, but not used, it is just a counter for how
		 * many entries are made within one day */
		timestamp.tm_hour = (int)strtol(dirlist->d_name, NULL, 10);
		strlcpy(pathbuf, path, sizeof(pathbuf));
		strlcat(pathbuf, "/", sizeof(pathbuf));
		strlcat(pathbuf, dirlist->d_name, sizeof(pathbuf));
		ret = gemlog_readdir_num(feed, timestamp, pathbuf);
		if (ret == NULL) {
			closedir(directory);
			return NULL;
		}
		feed = ret;
		dirlist = readdir(directory);
	}

	closedir(directory);
	return ret;
}

static struct gemlog_entry **
gemlog_readdir_num(struct gemlog_entry **feed, struct tm timestamp, const char *path)
{
	int fd;
	DIR *directory;
	struct gemlog_entry **ret;
	struct gemlog_entry *entry;
	struct dirent *dirlist;
	struct stat fst;
	char pathbuf[PATH_MAX+1];

	directory = opendir(path);
	if (directory == NULL)
		return NULL;

	entry = malloc(sizeof(*entry));
	if (entry == NULL) {
		closedir(directory);
		return NULL;
	}
	memset(entry, 0, sizeof(*entry));

	entry->date = timestamp;

	dirlist = readdir(directory);
	while (dirlist != NULL) {
		if (strcmp(dirlist->d_name, ".") == 0 || strcmp(dirlist->d_name, "..") == 0) {
			dirlist = readdir(directory);
			continue;
		}
		if (strcmp(dirlist->d_name, GEMLOG_TITLE_FILENAME) == 0) {
			strlcpy(pathbuf, path, sizeof(pathbuf));
			strlcat(pathbuf, "/", sizeof(pathbuf));
			strlcat(pathbuf, dirlist->d_name, sizeof(pathbuf));
			fd = open(pathbuf, O_RDONLY);
			if (fd == -1) {
				gemlog_entry_free(entry);
				closedir(directory);
				return NULL;
			}
			if (fstat(fd, &fst) == -1) {
				gemlog_entry_free(entry);
				close(fd);
				closedir(directory);
				return NULL;
			}
			entry->title = malloc(fst.st_size+1);
			if (entry->title == NULL) {
				gemlog_entry_free(entry);
				close(fd);
				closedir(directory);
				return NULL;
			}
			memset(entry->title, 0, fst.st_size+1);
			if (read(fd, entry->title, fst.st_size) == -1) {
				gemlog_entry_free(entry);
				close(fd);
				closedir(directory);
				return NULL;
			}
			close(fd);
		}
		if (strcmp(dirlist->d_name, GEMLOG_CONTENT_FILENAME) == 0) {
			strlcpy(pathbuf, path, sizeof(pathbuf));
			strlcat(pathbuf, "/", sizeof(pathbuf));
			strlcat(pathbuf, dirlist->d_name, sizeof(pathbuf));
			entry->content = gemtext_list_decode_file(pathbuf);
			if (entry->content == NULL) {
				gemlog_entry_free(entry);
				closedir(directory);
				return NULL;
			}
		}
		dirlist = readdir(directory);
	}

	ret = gemlog_entry_list_append(feed, entry);
	if (ret == NULL) {
		gemlog_entry_free(entry);
		closedir(directory);
		return NULL;
	}

	closedir(directory);
	return ret;
}

struct gemlog_entry **
gemlog_readdir(const char *path)
{
	struct tm timestamp;
	struct gemlog_entry **ret;

	memset(&timestamp, 0, sizeof(timestamp));
	ret = gemlog_readdir_root(NULL, timestamp, path);
	if (ret == NULL)
		return NULL;

	return ret;
}