#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(×tamp, 0, sizeof(timestamp));
ret = gemlog_readdir_root(NULL, timestamp, path);
if (ret == NULL)
return NULL;
return ret;
}