summary refs log tree commit diff stats
path: root/gemlog.c
diff options
context:
space:
mode:
Diffstat (limited to 'gemlog.c')
-rw-r--r--gemlog.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/gemlog.c b/gemlog.c
new file mode 100644
index 0000000..650a28b
--- /dev/null
+++ b/gemlog.c
@@ -0,0 +1,288 @@
+#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;
+}