agendafs

A filesystem for your calendar.

git clone git://mccd.space/agendafs
commit ab5df2237f20b676f1e110c40078beb3d1a860ee
parent 14f9a718a0e5503115702044bcea54fc34788fe3
Author: Marc Coquand <marc@coquand.email>
Date:   Sun,  1 Jun 2025 20:50:31 +0100

Add ability to create file

Diffstat:
MMakefile | 6+++---
Mmain.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mshell.nix | 1+
3 files changed, 86 insertions(+), 27 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,14 +1,14 @@
 CC = gcc
 CFLAGS = -Wall -O3
 CFLAGS_DEBUG = -Wall -g
-LIBS = `pkg-config fuse3 --cflags --libs`
+LIBS = `pkg-config uuid fuse3 --cflags --libs`
 
 all: caldavfs
 
-caldavfs: main.c hashmap.c
+caldavfs: main.c util.c hashmap.c
 	$(CC) $(CFLAGS) main.c util.c hashmap.c -o caldavfs $(LIBS)
 
-debug: main.c hashmap.c
+debug: main.c util.c hashmap.c
 	$(CC) $(CFLAGS_DEBUG) $(DEBUG_FLAGS) main.c util.c hashmap.c -o caldavfs-debug $(LIBS)
 
 clean:
diff --git a/main.c b/main.c
@@ -15,6 +15,8 @@
 #include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
+#include <uuid/uuid.h>
+
 #define ICS_DIR "/home/mccd/dev/fuse-journal/sample_ics"
 #define LOG(fmt, ...)                                                          \
 	fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
@@ -52,8 +54,13 @@ free_journal_entry(void *val)
 	free(entry->uid);
 	free(entry->filename);
 	free(entry->filename_original);
-	free(entry->summary);
-	free(entry->description);
+	if (entry->summary != NULL && strcmp(entry->summary, "") != 0) {
+		free(entry->summary);
+	}
+
+	if (entry->description != NULL && strcmp(entry->description, "") != 0) {
+		free(entry->description);
+	}
 	free(entry);
 }
 
@@ -716,8 +723,13 @@ parse_entry_content(journal_entry *entry, const char *buf, size_t size)
 	summary = strdup(summary_line);
 
 	// Clean up old values
-	free(entry->summary);
-	free(entry->description);
+	if (entry->summary != NULL && strcmp(entry->summary, "") != 0) {
+		free(entry->summary);
+	}
+
+	if (entry->description != NULL && strcmp(entry->description, "") != 0) {
+		free(entry->description);
+	}
 	entry->summary = summary;
 	entry->description = strdup(description);
 	entry->modified_at = time(NULL);
@@ -766,6 +778,7 @@ journal_write(const char *path, const char *buf, size_t size, off_t offset,
 	}
 
 	// Update hashmap with input
+	LOG("Parsing entry content");
 	int ret = parse_entry_content(entry, buf, size);
 	if (ret != 0) {
 		pthread_mutex_unlock(&entries_mutex);
@@ -803,18 +816,9 @@ copy_journal_entry(const journal_entry *src)
 time_t
 timestamp_from_filename(const char *filename)
 {
-
-	const char *basename = strrchr(filename, '/');
-	if (basename != NULL) {
-		basename++;
-	}
-	else {
-		basename = filename;
-	}
-
 	regmatch_t matches[3];
 
-	if (regexec(&regex_fuse_file, basename, 3, matches, 0) != 0)
+	if (regexec(&regex_fuse_file, filename, 3, matches, 0) != 0)
 		return INVALID_TIME;
 
 	// Extract YYYYMMDD and HH:MM
@@ -823,10 +827,10 @@ timestamp_from_filename(const char *filename)
 
 	snprintf(date_str, sizeof(date_str), "%.*s",
 		 matches[1].rm_eo - matches[1].rm_so,
-		 basename + matches[1].rm_so);
+		 filename + matches[1].rm_so);
 	snprintf(time_str, sizeof(time_str), "%.*s",
 		 matches[2].rm_eo - matches[2].rm_so,
-		 basename + matches[2].rm_so);
+		 filename + matches[2].rm_so);
 
 	int year, month, day, hour, minute;
 	if (sscanf(date_str, "%4d%2d%2d", &year, &month, &day) != 3)
@@ -892,11 +896,12 @@ static int
 journal_rename(const char *old, const char *new, unsigned int flags)
 {
 	pthread_mutex_lock(&entries_mutex);
-	LOG("Renaming %s -> %s", old, new);
+	LOG("RENAME %s -> %s", old, new);
 
 	// Strip path, validate just the new basename
 	const char *new_basename = strrchr(new, '/');
-	time_t new_created_at = timestamp_from_filename(new);
+	new_basename++;
+	time_t new_created_at = timestamp_from_filename(new_basename);
 
 	if (new_created_at == INVALID_TIME) {
 		LOG("Invalid filename format: %s", new_basename);
@@ -910,6 +915,65 @@ journal_rename(const char *old, const char *new, unsigned int flags)
 	return res;
 }
 
+//  uuid-app.ics
+char *
+create_new_unique_ics_uid()
+{
+	uuid_t uuid;
+	char uuid_str[37]; // UUIDs are 36 characters + null terminator
+	uuid_generate(uuid);
+	uuid_unparse(uuid, uuid_str);
+	char *res = NULL;
+	if (asprintf(&res, "%s-caldavfs", uuid_str) == -1) {
+		return NULL;
+	}
+	return res;
+}
+
+int
+journal_create(const char *filename, mode_t mode, struct fuse_file_info *info)
+{
+	pthread_mutex_lock(&entries_mutex);
+	LOG("CREATE %s", filename);
+
+	// Strip path, validate just the new basename
+	const char *new_basename = strrchr(filename, '/');
+	new_basename++;
+	time_t created_at = timestamp_from_filename(new_basename);
+
+	if (created_at == INVALID_TIME) {
+		LOG("Invalid filename format: %s", new_basename);
+		pthread_mutex_unlock(&entries_mutex);
+		return -EINVAL;
+	}
+
+	journal_entry *new_entry = xcalloc(1, sizeof(journal_entry));
+	new_entry->filename = strdup(new_basename);
+	new_entry->created_at = created_at;
+	new_entry->modified_at = created_at;
+	new_entry->summary = "";
+	new_entry->description = "";
+	new_entry->uid = create_new_unique_ics_uid();
+	asprintf(&new_entry->filename_original, "%s.ics", new_entry->uid);
+
+	hashmap_insert(entries_original_key, new_entry->filename_original,
+		       new_entry);
+	hashmap_insert(entries_fuse_key, new_entry->filename, new_entry);
+
+	// Write back to the original
+	char *filepath = get_original_filepath(new_entry);
+
+	if (write_entry_to_ical_file(new_entry, filepath) != 0) {
+		free(filepath);
+		return -EIO;
+	}
+
+	free(filepath);
+
+	pthread_mutex_unlock(&entries_mutex);
+	return 0;
+}
+
 void
 update_fuse_entry_from_original(const char *filename)
 {
@@ -1013,12 +1077,6 @@ watch_ics_dir(void *arg)
 	return NULL;
 }
 
-int
-journal_create(const char *file_name, mode_t mode, struct fuse_file_info *info)
-{
-	return -EIO;
-}
-
 static const struct fuse_operations journal_oper = {.getattr = journal_getattr,
 						    .readdir = journal_readdir,
 						    .open = journal_open,
diff --git a/shell.nix b/shell.nix
@@ -13,5 +13,6 @@ pkgs.mkShell {
     gdb
     pkg-config
     valgrind
+    libuuid
   ];
 }