agendafs
A filesystem for your calendar.
git clone git://mccd.space/agendafscommit b04e20707adcb9b5ef486f83f3a3dc076a0f81d2
parent e746486aefcd47399704b99a3c664e72a44f0578
Author: Marc Coquand <marc@coquand.email>
Date: Wed, 9 Jul 2025 23:38:27 +0200
Implement get category property with xattr
Still missing set and delete.
Implementation uses memstreams to make it simpler to append to
string
References: https://todo.sr.ht/~marcc/agendafs/1
Diffstat:
3 files changed, 102 insertions(+), 16 deletions(-)
diff --git a/journal_entry.c b/journal_entry.c
@@ -235,7 +235,7 @@ free_segments(char **segments, size_t count)
}
struct tree_node *
-get_node_by_path(struct tree_node *root, const char *path)
+get_node_by_path(const char *path)
{
if (strcmp(path, "/") == 0) {
LOG("Is ROOT %s", path);
@@ -248,7 +248,7 @@ get_node_by_path(struct tree_node *root, const char *path)
return fuse_tree_root;
LOG("Segments are defined");
- struct tree_node *current = root;
+ struct tree_node *current = fuse_tree_root;
for (size_t i = 0; i < count && current; ++i) {
const char *segment = segments[i];
@@ -317,7 +317,7 @@ get_entry_from_fuse_path(const char *path)
LOG("Invalid path: '%s'\n", path ? path : "NULL");
return NULL;
}
- const struct tree_node *node = get_node_by_path(fuse_tree_root, path);
+ const struct tree_node *node = get_node_by_path(path);
if (!node || !node->data) {
LOG("No entry found for key: '%s'\n", path);
@@ -723,6 +723,67 @@ copy_journal_entry(const struct journal_entry *src)
return copy;
}
+static int
+append_category_to_memstream(FILE *memstream, const char *category,
+ bool is_first_category)
+{
+ if (!is_first_category) {
+ if (fputs(",", memstream) == EOF) {
+ LOG("FPUTS FAILED");
+ return -1;
+ }
+ }
+
+ if (fputs(category, memstream) == EOF) {
+ LOG("FPUTS FAILED");
+ return -1;
+ }
+ return 0; // Success
+}
+
+// Returns comma separated list of categories for a file
+const char *
+get_node_categories(const struct tree_node *node)
+{
+ const struct journal_entry *entry = node->data;
+ if (!entry) {
+ return "\0";
+ }
+
+ FILE *memstream = NULL;
+ char *result_buffer = NULL;
+ size_t buffer_size = 0;
+
+ memstream = open_memstream(&result_buffer, &buffer_size);
+
+ icalcomponent *inner = icalcomponent_get_inner(entry->component);
+
+ icalproperty *catp =
+ icalcomponent_get_first_property(inner, ICAL_CATEGORIES_PROPERTY);
+
+ bool first_category = true;
+ while (catp) {
+ const char *category = icalproperty_get_categories(catp);
+ if (append_category_to_memstream(memstream, category,
+ first_category) == -1) {
+ fclose(memstream);
+ free(result_buffer);
+ return "";
+ }
+ catp = icalcomponent_get_next_property(
+ inner, ICAL_CATEGORIES_PROPERTY);
+
+ first_category = false;
+ }
+
+ if (fclose(memstream) == EOF) {
+ free(result_buffer);
+ return NULL; // Error during close
+ }
+
+ return result_buffer;
+}
+
size_t
write_entry_to_ical_file(const struct journal_entry *entry)
{
@@ -802,8 +863,7 @@ do_journal_entry_rename(const char *old, const char *new)
char *parent_path = NULL;
get_parent_path(new, &parent_path);
LOG("PARENT PATH: %s", parent_path);
- struct tree_node *new_parent_node =
- get_node_by_path(fuse_tree_root, parent_path);
+ struct tree_node *new_parent_node = get_node_by_path(parent_path);
free(parent_path);
if (new_parent_node == NULL) {
@@ -820,7 +880,7 @@ do_journal_entry_rename(const char *old, const char *new)
*dot = '\0';
}
LOG("Summary is now %s", new_summary);
- struct tree_node *entry_node = get_node_by_path(fuse_tree_root, old);
+ struct tree_node *entry_node = get_node_by_path(old);
if (!entry_node) {
free(new_copy);
free(new_summary);
@@ -998,8 +1058,7 @@ create_entry_from_fuse(const char *fuse_path)
char *parent_path = NULL;
get_parent_path(fuse_path, &parent_path);
LOG("Parent path is %s", parent_path);
- struct tree_node *parent =
- get_node_by_path(fuse_tree_root, parent_path);
+ struct tree_node *parent = get_node_by_path(parent_path);
free(parent_path);
if (!parent) {
@@ -1055,8 +1114,7 @@ create_directory_from_fuse_path(const char *fuse_path)
char *parent_path = NULL;
get_parent_path(fuse_path, &parent_path);
LOG("Parent path is %s", parent_path);
- struct tree_node *parent =
- get_node_by_path(fuse_tree_root, parent_path);
+ struct tree_node *parent = get_node_by_path(parent_path);
free(parent_path);
if (!parent) {
@@ -1142,7 +1200,7 @@ int
delete_dir_from_fuse_path(const char *filepath)
{
int res = 0;
- struct tree_node *node = get_node_by_path(fuse_tree_root, filepath);
+ struct tree_node *node = get_node_by_path(filepath);
if (!node) {
LOG("Node not found");
return -EIO;
@@ -1180,7 +1238,7 @@ int
delete_from_fuse_path(const char *filepath)
{
int res = 0;
- struct tree_node *node = get_node_by_path(fuse_tree_root, filepath);
+ struct tree_node *node = get_node_by_path(filepath);
if (!node) {
return -EIO;
}
diff --git a/journal_entry.h b/journal_entry.h
@@ -134,7 +134,10 @@ int
build_today_hashmap();
struct tree_node *
-get_node_by_path(struct tree_node *root, const char *path);
+get_node_by_path(const char *path);
+
+const char *
+get_node_categories(const struct tree_node *);
int
build_notes_hashmap();
diff --git a/main.c b/main.c
@@ -47,7 +47,7 @@ journal_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
goto unlock_and_return;
}
- const struct tree_node *node = get_node_by_path(fuse_tree_root, path);
+ const struct tree_node *node = get_node_by_path(path);
if (!node) {
LOG("Entry not found");
ret_code = -ENOENT;
@@ -131,8 +131,7 @@ journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
}
}
else {
- const struct tree_node *node =
- get_node_by_path(fuse_tree_root, path);
+ const struct tree_node *node = get_node_by_path(path);
if (node) {
for (size_t i = 0; i < node->child_count; i++) {
const struct tree_node *child =
@@ -415,6 +414,29 @@ journal_utimens(const char *path, const struct timespec tv[2],
return 0;
}
+static int
+journal_getxattr(const char *path, const char *header, char *buf, size_t s)
+{
+ LOG("GETXATTR '%s' '%s' '%zu'\n", path, header, s);
+
+ if (strcmp(header, "user.categories") != 0) {
+ return -ENODATA;
+ }
+
+ struct tree_node *node = get_node_by_path(path);
+
+ const char *cats = get_node_categories(node);
+
+ int string_len = strlen(cats);
+
+ // If s == 0 we don't need to write, just return size to allocate
+ if (s > 0) {
+ snprintf(buf, s, "%s", cats);
+ }
+
+ return string_len;
+}
+
struct fuse_operations journal_oper = {.getattr = journal_getattr,
.readdir = journal_readdir,
.open = journal_open,
@@ -424,6 +446,7 @@ struct fuse_operations journal_oper = {.getattr = journal_getattr,
.create = journal_create,
.mkdir = journal_mkdir,
.unlink = journal_unlink,
+ .getxattr = journal_getxattr,
.rmdir = journal_rmdir,
.readlink = journal_readlink,
.rename = journal_rename};
@@ -505,6 +528,8 @@ main(int argc, char *argv[])
exit(1);
}
+ LOG("LOADED ICS DIR");
+
if (pthread_create(&watcher_thread, NULL, watch_ics_dir, NULL) != 0) {
perror("Failed to create inotify watcher thread");
return 1;