agendafs
A filesystem for your calendar.
git clone git://mccd.space/agendafscommit 1aff4c96ef6d5f6deb6c34246f72b9aa544a34c2
parent 6a9c66a5235d7948ba935e1643b0ebc1be02880c
Author: Marc Coquand <marc@coquand.email>
Date: Fri, 27 Jun 2025 10:59:26 +0100
*
Diffstat:
| M | README.md | | | 25 | +++++++++++++++++++------ |
| M | journal_entry.c | | | 145 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
2 files changed, 134 insertions(+), 36 deletions(-)
diff --git a/README.md b/README.md
@@ -44,17 +44,30 @@ These are not really intended to be edited by users.
- [x] VJOURNAL - Now the first true journaling filesystem!
- [x] Create
- [x] Create remote
- - [x] Delete locally
+ - [ ] Delete locally
- [x] Delete remote
- [x] Read
- - [x] Rename
+ - [ ] Rename
- [x] Write
- [x] Set creation dates - not mandatory but supresses a few warnings
- - [ ] Directory structure, children are VJOURNALs with parent as VJOURNAL
- - [ ] Use DTSTART;VALUE=DATE: for journal to separate them from notes.
-- [ ] Categories
+ - [x] Directory structure, children are VJOURNALs with parent as VJOURNAL
+- [ ] Categories
+- [ ] Parse files on demand instead of keeping everything in memory
+
+Right now we parse all files and keep in memory. Instead, we should parse them once, compute the file tree from that, and then just parse directories on demand with a cache.
+
+- [ ] Views
+
+A directory `.views` where representations of the caldavfs is kept.
+
+For example:
+- TODOs
+- Due todos soon
+- Agenda
+- DONE items
+
- [ ] VTODO
- - [ ] Embed in VJOURNAL using parent relationship
+ - [ ] Design.
- [ ] VEVENTS
- [ ] Design.
- [ ] VALARM
diff --git a/journal_entry.c b/journal_entry.c
@@ -92,6 +92,17 @@ get_node_children(tree_node *node)
return entries;
}
+void
+detach_node_and_entry(tree_node *node)
+{
+ tree_node *parent = node->parent;
+ if (!parent || !parent->data) {
+ return;
+ }
+ // TODO: Remove parent child relationship
+ detach_tree_node(node);
+}
+
tree_node *
get_node_by_uuid(const char *target_uuid)
{
@@ -106,6 +117,14 @@ get_node_by_uuid(const char *target_uuid)
tree_node *node = hashmap_get(entries_original_ics, filename_original);
return node;
}
+bool
+is_root_node(tree_node *node)
+{
+ if (!node->data) {
+ return true;
+ }
+ return false;
+}
char **
split_path(const char *path, size_t *count_out)
@@ -148,11 +167,59 @@ set_parent_child_relationship_to_component(icalcomponent *parent,
icalcomponent_add_property(parent, parent_related_to_child);
}
+
+void
+remove_parent_child_relationship_from_component(icalcomponent *parent,
+ icalcomponent *child)
+{
+
+ const char *parent_uid = icalcomponent_get_uid(parent);
+ const char *child_uid = icalcomponent_get_uid(child);
+
+ // Remove from child: RELATED-TO:parent_uid;RELTYPE=PARENT
+ icalproperty *prop =
+ icalcomponent_get_first_property(child, ICAL_RELATEDTO_PROPERTY);
+ while (prop != NULL) {
+ icalproperty *next = icalcomponent_get_next_property(
+ child, ICAL_RELATEDTO_PROPERTY);
+ const char *value = icalproperty_get_relatedto(prop);
+ icalparameter *reltype = icalproperty_get_first_parameter(
+ prop, ICAL_RELTYPE_PARAMETER);
+
+ if (value && strcmp(value, parent_uid) == 0 && reltype &&
+ icalparameter_get_reltype(reltype) == ICAL_RELTYPE_PARENT) {
+ icalcomponent_remove_property(child, prop);
+ icalproperty_free(prop);
+ }
+
+ prop = next;
+ }
+
+ // Remove from parent: RELATED-TO:child_uid;RELTYPE=CHILD
+ prop =
+ icalcomponent_get_first_property(parent, ICAL_RELATEDTO_PROPERTY);
+ while (prop != NULL) {
+ icalproperty *next = icalcomponent_get_next_property(
+ parent, ICAL_RELATEDTO_PROPERTY);
+ const char *value = icalproperty_get_relatedto(prop);
+ icalparameter *reltype = icalproperty_get_first_parameter(
+ prop, ICAL_RELTYPE_PARAMETER);
+
+ if (value && strcmp(value, child_uid) == 0 && reltype &&
+ icalparameter_get_reltype(reltype) == ICAL_RELTYPE_CHILD) {
+ icalcomponent_remove_property(parent, prop);
+ icalproperty_free(prop);
+ }
+
+ prop = next;
+ }
+}
+
void
add_child_to_node(tree_node *parent, tree_node *child)
{
// Root node, don't add relationship to component
- if (parent->data == NULL) {
+ if (is_root_node(parent)) {
add_child(parent, child);
return;
}
@@ -395,17 +462,21 @@ parse_ics_to_journal_entry_component(const char *filename,
return 0;
}
-char *
-time_string(icaltimetype t, const char *format)
+void
+move_node(tree_node *old_parent, tree_node *new_parent, tree_node *child)
{
-
- char *buffer = xmalloc(64);
- time_t tme = icaltime_as_timet(t);
- struct tm tm_info;
- localtime_r(&tme, &tm_info);
-
- strftime(buffer, 64, format, &tm_info);
- return buffer;
+ struct journal_entry *child_entry = child->data;
+
+ // Not root node
+ if (!is_root_node(old_parent)) {
+ LOG("Removing old node");
+ struct journal_entry *old_parent_entry = old_parent->data;
+ remove_parent_child_relationship_from_component(
+ old_parent_entry->component, child_entry->component);
+ write_entry_to_ical_file(old_parent_entry);
+ }
+ detach_tree_node(child);
+ add_child_to_node(new_parent, child);
}
// Returns -1 on failure, 0 on success
@@ -697,8 +768,16 @@ do_journal_entry_rename(const char *old, const char *new)
}
LOG("Summary is now %s", new_summary);
tree_node *entry_node = get_node_by_path(fuse_tree_root, old);
- struct journal_entry *old_entry = entry_node->data;
+ if (!entry_node) {
+ free(new_copy);
+ free(new_summary);
+
+ return -EIO;
+ }
+
+ tree_node *old_parent_node = entry_node->parent;
+ struct journal_entry *old_entry = entry_node->data;
if (!old_entry) {
LOG("Entry not found '%s'", old);
free(new_copy);
@@ -713,7 +792,15 @@ do_journal_entry_rename(const char *old, const char *new)
return -EIO;
}
- icalcomponent_set_summary(new_entry->component, new_summary);
+ char *parent_path = NULL;
+ get_parent_path(new, &parent_path);
+ LOG("PARENT PATH: %s", parent_path);
+ tree_node *new_parent_node =
+ get_node_by_path(fuse_tree_root, parent_path);
+ free(parent_path);
+
+ move_node(old_parent_node, new_parent_node, entry_node);
+
// TODO: Filename must be unique
asprintf(&new_entry->filename, "%s", new_filename);
LOG("New filename is now %s", new_filename);
@@ -724,10 +811,11 @@ do_journal_entry_rename(const char *old, const char *new)
hashmap_insert(entries_original_ics, new_entry->filename_original,
entry_node);
+ icalcomponent_set_summary(new_entry->component, new_summary);
entry_node->data = new_entry;
- free_journal_entry(old_entry);
-
write_entry_to_ical_file(new_entry);
+
+ free_journal_entry(old_entry);
free(new_copy);
return 0;
@@ -860,24 +948,21 @@ create_entry_from_fuse(const char *fuse_path)
tree_node *new_node = create_file_node(new_entry);
char *parent_path = NULL;
- if (get_parent_path(fuse_path, &parent_path) == 0) {
- add_child_to_node(fuse_tree_root, new_node);
- }
- else {
- tree_node *parent =
- get_node_by_path(fuse_tree_root, parent_path);
- free(parent_path);
-
- if (!parent || !entry_is_directory(parent)) {
+ get_parent_path(fuse_path, &parent_path);
+ LOG("Parent path is %s", parent_path);
+ tree_node *parent = get_node_by_path(fuse_tree_root, parent_path);
+ free(parent_path);
- return -ENOENT;
- }
- else if (!entry_is_directory(parent)) {
- return -ENOTDIR;
- }
- add_child_to_node(parent, new_node);
+ if (!parent) {
+ LOG("Parent is not node");
+ return -ENOENT;
}
+ if (!entry_is_directory(parent)) {
+ LOG("Parent is not directory ");
+ return -ENOTDIR;
+ }
+ add_child_to_node(parent, new_node);
hashmap_insert(entries_original_ics, new_entry->filename_original,
new_node);