agendafs

A filesystem for your calendar.

git clone git://mccd.space/agendafs
commit a1671013c01a9540c78910e978f23c9f4df5da82
parent 0687368b70be16eb0593c1de843686501db9ee69
Author: Marc Coquand <marc@coquand.email>
Date:   Thu,  5 Jun 2025 21:08:42 +0100

Add a today directory with summary as title

Diffstat:
Mmain.c | 140++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 82 insertions(+), 58 deletions(-)
diff --git a/main.c b/main.c
@@ -42,8 +42,14 @@ typedef struct {
 } journal_entry;
 
 // We create two maps that link to the same entries
-struct hashmap *entries_fuse_key = NULL;
-struct hashmap *entries_original_key = NULL;
+// journal_entry
+struct hashmap *entries_fuse = NULL;
+
+// journal_entry
+struct hashmap *entries_original_ics = NULL;
+
+// string -> string
+struct hashmap *entries_today = NULL;
 
 static pthread_mutex_t entries_mutex = PTHREAD_MUTEX_INITIALIZER;
 icaltimetype
@@ -113,7 +119,7 @@ get_entry_from_fuse_path(const char *path)
 	}
 
 	LOG("Looking up key: '%s'\n", key);
-	journal_entry *entry = hashmap_get(entries_fuse_key, key);
+	journal_entry *entry = hashmap_get(entries_fuse, key);
 
 	if (!entry) {
 		LOG("No entry found for key: '%s'\n", key);
@@ -233,8 +239,8 @@ load_journal_entries()
 {
 	pthread_mutex_lock(&entries_mutex);
 
-	entries_original_key = hashmap_new(NULL);
-	entries_fuse_key = hashmap_new(free_journal_entry);
+	entries_original_ics = hashmap_new(NULL);
+	entries_fuse = hashmap_new(free_journal_entry);
 
 	LOG("Loading journal entries from: %s\n", ICS_DIR);
 
@@ -257,10 +263,9 @@ load_journal_entries()
 				continue;
 			}
 
-			if (hashmap_insert(entries_fuse_key,
-					   new_entry->filename,
+			if (hashmap_insert(entries_fuse, new_entry->filename,
 					   new_entry) != 0 ||
-			    hashmap_insert(entries_original_key,
+			    hashmap_insert(entries_original_ics,
 					   new_entry->filename_original,
 					   new_entry) != 0) {
 				LOG("Failed to insert entry");
@@ -348,16 +353,8 @@ journal_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
 	const char *today_prefix = "/today/";
 	if (strncmp(path, today_prefix, strlen(today_prefix)) == 0) {
 		const char *key = path + strlen(today_prefix);
-		journal_entry *entry = hashmap_get(entries_fuse_key, key);
-
-		if (!entry) {
-			ret_code = -ENOENT;
-			goto unlock_and_return;
-		}
-
-		icalcomponent *component = entry->component;
-		struct icaltimetype dt = icalcomponent_get_dtstamp(component);
-		if (icaltime_compare_date_only(dt, icaltime_today()) != 0) {
+		char *entry_filename = hashmap_get(entries_today, key);
+		if (!entry_filename) {
 			ret_code = -ENOENT;
 			goto unlock_and_return;
 		}
@@ -405,19 +402,62 @@ journal_readlink(const char *path, char *buf, size_t size)
 		pthread_mutex_lock(&entries_mutex);
 		const char *key = path + strlen(today_prefix);
 		LOG("Lookup %s", key);
-		journal_entry *entry = hashmap_get(entries_fuse_key, key);
+		char *link = hashmap_get(entries_today, key);
 
-		if (!entry)
+		if (!link) {
+			LOG("Link not found for %s", key);
 			return -ENOENT;
+		}
+		LOG("Link found %s", link);
 
 		// Return symlink target
-		snprintf(buf, size, "../entries/%s", key);
+		snprintf(buf, size, "%s", link);
 
 		pthread_mutex_unlock(&entries_mutex);
 		return 0;
 	}
 	return -ENOENT;
 }
+
+int
+buildTodayHashmap()
+{
+	hashmap_free(entries_today);
+	entries_today = hashmap_new(free);
+
+	size_t n_keys = 0;
+	char **keys = hashmap_get_keys(entries_fuse, &n_keys);
+	if (!keys) {
+		return -ENOMEM;
+	}
+
+	for (size_t i = 0; i < n_keys; i++) {
+		journal_entry *entry = hashmap_get(entries_fuse, keys[i]);
+		if (!entry)
+			continue;
+
+		// Get the dtstamp of the first icalcomponent
+		icalcomponent *component = entry->component;
+		if (!component)
+			continue;
+
+		struct icaltimetype dt = icalcomponent_get_dtstamp(component);
+
+		if (icaltime_compare_date_only(dt, icaltime_today()) == 0) {
+			char *filename =
+			    strdup(icalcomponent_get_summary(component));
+			char *dest;
+			if (asprintf(&dest, "../entries/%s", entry->filename) ==
+			    -1)
+				return -ENOMEM;
+			hashmap_insert(entries_today, filename, dest);
+		}
+	}
+
+	hashmap_free_keys(keys, n_keys);
+	return 0;
+}
+
 int
 journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 		off_t offset, struct fuse_file_info *fi,
@@ -436,7 +476,7 @@ journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 	}
 	else if (strcmp(path, "/entries") == 0) {
 		size_t n_keys = 0;
-		char **keys = hashmap_get_keys(entries_fuse_key, &n_keys);
+		char **keys = hashmap_get_keys(entries_fuse, &n_keys);
 		if (!keys) {
 			pthread_mutex_unlock(&entries_mutex);
 			return -ENOMEM;
@@ -450,30 +490,13 @@ journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 	}
 	else if (strcmp(path, "/today") == 0) {
 		size_t n_keys = 0;
-		char **keys = hashmap_get_keys(entries_fuse_key, &n_keys);
+		buildTodayHashmap();
+		char **keys = hashmap_get_keys(entries_today, &n_keys);
 		if (!keys) {
-			pthread_mutex_unlock(&entries_mutex);
 			return -ENOMEM;
 		}
-
 		for (size_t i = 0; i < n_keys; i++) {
-			journal_entry *entry =
-			    hashmap_get(entries_fuse_key, keys[i]);
-			if (!entry)
-				continue;
-
-			// Get the dtstamp of the first icalcomponent
-			icalcomponent *component = entry->component;
-			if (!component)
-				continue;
-
-			struct icaltimetype dt =
-			    icalcomponent_get_dtstamp(component);
-
-			if (icaltime_compare_date_only(dt, icaltime_today()) ==
-			    0) {
-				filler(buf, keys[i], NULL, 0, 0);
-			}
+			filler(buf, keys[i], NULL, 0, 0);
 		}
 
 		hashmap_free_keys(keys, n_keys);
@@ -691,7 +714,7 @@ do_journal_entry_rename(time_t new_created_at, const char *old, const char *new)
 	const char *new_basename = strrchr(new, '/');
 	new_basename = new_basename + 1;
 
-	journal_entry *old_entry = hashmap_get(entries_fuse_key, old_basename);
+	journal_entry *old_entry = hashmap_get(entries_fuse, old_basename);
 	if (!old_entry) {
 		LOG("Entry does not exist");
 		return -EINVAL;
@@ -705,13 +728,13 @@ do_journal_entry_rename(time_t new_created_at, const char *old, const char *new)
 
 	icalcomponent_set_dtstamp(new_entry->component, created_at_ical);
 
-	hashmap_remove(entries_original_key, old_entry->filename_original);
+	hashmap_remove(entries_original_ics, old_entry->filename_original);
 	// remove handles freeing here
-	hashmap_remove(entries_fuse_key, old_entry->filename);
+	hashmap_remove(entries_fuse, old_entry->filename);
 
-	hashmap_insert(entries_original_key, new_entry->filename_original,
+	hashmap_insert(entries_original_ics, new_entry->filename_original,
 		       new_entry);
-	hashmap_insert(entries_fuse_key, new_entry->filename, new_entry);
+	hashmap_insert(entries_fuse, new_entry->filename, new_entry);
 
 	if (write_entry_to_ical_file(new_entry) != 0) {
 		return -EIO;
@@ -732,7 +755,7 @@ journal_unlink(const char *file)
 	const char *keyname = strrchr(file, '/');
 	keyname++;
 
-	journal_entry *entry = hashmap_get(entries_fuse_key, keyname);
+	journal_entry *entry = hashmap_get(entries_fuse, keyname);
 	if (!entry) {
 		LOG("Entry does not exist");
 		pthread_mutex_unlock(&entries_mutex);
@@ -757,9 +780,9 @@ journal_unlink(const char *file)
 		return -EIO;
 	}
 
-	hashmap_remove(entries_original_key, keyname);
+	hashmap_remove(entries_original_ics, keyname);
 	// Frees
-	hashmap_remove(entries_fuse_key, keyname);
+	hashmap_remove(entries_fuse, keyname);
 	free(filepath);
 
 	LOG("Delete successful");
@@ -864,9 +887,9 @@ journal_create(const char *filename, mode_t mode, struct fuse_file_info *info)
 	}
 	LOG("Inserting vjournal entry");
 
-	hashmap_insert(entries_original_key, new_entry->filename_original,
+	hashmap_insert(entries_original_ics, new_entry->filename_original,
 		       new_entry);
-	hashmap_insert(entries_fuse_key, new_entry->filename, new_entry);
+	hashmap_insert(entries_fuse, new_entry->filename, new_entry);
 
 	// Write back to the original
 	char *filepath = get_original_filepath(new_entry);
@@ -903,9 +926,9 @@ update_or_create_fuse_entry_from_original(const char *filename)
 	pthread_mutex_unlock(&entries_mutex);
 	return;
 
-	hashmap_insert(entries_original_key, new_entry->filename_original,
+	hashmap_insert(entries_original_ics, new_entry->filename_original,
 		       new_entry);
-	hashmap_insert(entries_fuse_key, new_entry->filename, new_entry);
+	hashmap_insert(entries_fuse, new_entry->filename, new_entry);
 
 	LOG("File updated");
 
@@ -920,9 +943,9 @@ update_fuse_entry_delete(const char *file)
 	const char *keyname = strrchr(file, '/');
 	keyname++;
 	LOG("Found entry to delete");
-	hashmap_remove(entries_original_key, keyname);
+	hashmap_remove(entries_original_ics, keyname);
 	// Frees
-	hashmap_remove(entries_fuse_key, keyname);
+	hashmap_remove(entries_fuse, keyname);
 	pthread_mutex_unlock(&entries_mutex);
 	return 0;
 }
@@ -1079,8 +1102,9 @@ main(int argc, char *argv[])
 	pthread_join(watcher_thread, NULL); // Clean up
 	LOG("Pthread freed");
 
-	hashmap_free(entries_fuse_key);
-	hashmap_free(entries_original_key);
+	hashmap_free(entries_fuse);
+	hashmap_free(entries_original_ics);
+	hashmap_free(entries_today);
 	LOG("Hashmap freed");
 
 	regfree(&regex_fuse_file);