diff options
Diffstat (limited to 'gemlog.c')
-rw-r--r-- | gemlog.c | 288 |
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(×tamp, 0, sizeof(timestamp)); + ret = gemlog_readdir_root(NULL, timestamp, path); + if (ret == NULL) + return NULL; + + return ret; +} |