agendafs
A filesystem for your calendar.
git clone git://mccd.space/agendafscommit 9666d06c8660caedcd5f0bffa70bd951a5f6abcb
parent 9b5cb3d2381f1cca01e06d8cc4838a46a6659e1e
Author: Marc Coquand <marc@coquand.email>
Date: Fri, 30 May 2025 19:24:07 +0100
Improvements
Diffstat:
2 files changed, 148 insertions(+), 28 deletions(-)
diff --git a/main.c b/main.c
@@ -8,6 +8,7 @@
#include <errno.h>
#include <time.h>
#include <sys/stat.h>
+#include <regex.h>
#define ICS_DIR "./sample_ics"
@@ -21,6 +22,108 @@ typedef struct {
static journal_entry *entries = NULL;
static size_t entry_count = 0;
+static void free_entry(journal_entry *entry) {
+ if (entry) {
+ free(entry->filename);
+ free(entry->summary);
+ free(entry->description);
+ }
+}
+
+static void free_all_entries() {
+ for (size_t i = 0; i < entry_count; i++) {
+ free_entry(&entries[i]);
+ }
+ free(entries);
+}
+
+static int parse_ics_file(const char *file_path, journal_entry *entry) {
+ FILE *file = fopen(file_path, "r");
+ if (!file) {
+ perror("Failed to open .ics file");
+ return -1;
+ }
+
+ // Read the whole file into a string
+ fseek(file, 0, SEEK_END);
+ long file_size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char *file_content = malloc(file_size + 1);
+ if (!file_content) {
+ perror("Memory allocation failed");
+ fclose(file);
+ return -1;
+ }
+ fread(file_content, 1, file_size, file);
+ file_content[file_size] = '\0'; // Null-terminate the string
+ fclose(file);
+
+ // Regular expressions for SUMMARY and DESCRIPTION fields
+ regex_t regex_summary, regex_description;
+ regmatch_t matches_summary[2], matches_description[2];
+
+ // Compile regular expressions
+ if (regcomp(®ex_summary, "SUMMARY:([^\r\n]+)", REG_EXTENDED) != 0 ||
+ regcomp(®ex_description, "DESCRIPTION:([^\n\r]*)", REG_EXTENDED) != 0) {
+ fprintf(stderr, "Failed to compile regular expressions\n");
+ free(file_content);
+ return -1;
+ }
+
+ // Extract the SUMMARY
+ if (regexec(®ex_summary, file_content, 2, matches_summary, 0) == 0) {
+ size_t summary_len = matches_summary[1].rm_eo - matches_summary[1].rm_so;
+ entry->summary = malloc(summary_len + 1);
+ if (!entry->summary) {
+ perror("Memory allocation failed for SUMMARY");
+ free(file_content);
+ regfree(®ex_summary);
+ regfree(®ex_description);
+ return -1;
+ }
+ strncpy(entry->summary, file_content + matches_summary[1].rm_so, summary_len);
+ entry->summary[summary_len] = '\0';
+ } else {
+ fprintf(stderr, "SUMMARY field not found in: %s\n", file_path);
+ free(file_content);
+ regfree(®ex_summary);
+ regfree(®ex_description);
+ return -1;
+ }
+
+ // Extract the DESCRIPTION
+ if (regexec(®ex_description, file_content, 2, matches_description, 0) == 0) {
+ size_t description_len = matches_description[1].rm_eo - matches_description[1].rm_so;
+ entry->description = malloc(description_len + 1);
+ if (!entry->description) {
+ perror("Memory allocation failed for DESCRIPTION");
+ free(file_content);
+ regfree(®ex_summary);
+ regfree(®ex_description);
+ return -1;
+ }
+ strncpy(entry->description, file_content + matches_description[1].rm_so, description_len);
+ entry->description[description_len] = '\0';
+ printf("Matched DESCRIPTION: %s\n", entry->description); // Debugging line
+ } else {
+ fprintf(stderr, "DESCRIPTION field not found in: %s\n", file_path);
+ free(file_content);
+ regfree(®ex_summary);
+ regfree(®ex_description);
+ return -1;
+ }
+
+ // Set timestamp to the current time
+ entry->timestamp = time(NULL);
+
+ // Clean up
+ free(file_content);
+ regfree(®ex_summary);
+ regfree(®ex_description);
+ return 0;
+}
+
static void load_journal_entries() {
printf("Loading journal entries from: %s\n", ICS_DIR);
DIR *dir = opendir(ICS_DIR);
@@ -34,29 +137,22 @@ static void load_journal_entries() {
while ((entry = readdir(dir))) {
if (entry->d_type == DT_REG && strstr(entry->d_name, ".ics")) {
snprintf(filepath, sizeof(filepath), "%s/%s", ICS_DIR, entry->d_name);
- FILE *file = fopen(filepath, "r");
- if (file) {
- // Simulate parsing a VJOURNAL from the .ics file
- printf("Found .ics file: %s\n", filepath);
+ journal_entry new_entry;
+ new_entry.filename = strdup(entry->d_name);
+ if (parse_ics_file(filepath, &new_entry) == 0) {
entries = realloc(entries, sizeof(journal_entry) * (entry_count + 1));
- entries[entry_count].filename = strdup(entry->d_name);
- entries[entry_count].summary = strdup("Mock summary from .ics");
- entries[entry_count].description = strdup("Mock description from .ics");
- entries[entry_count].timestamp = time(NULL);
+ entries[entry_count] = new_entry;
entry_count++;
- fclose(file);
} else {
- perror("fopen");
+ free(new_entry.filename);
}
}
}
-
printf("Loaded %zu journal entries.\n", entry_count);
closedir(dir);
}
static int journal_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) {
- printf("getattr called for: %s\n", path);
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
@@ -82,7 +178,6 @@ static int journal_getattr(const char *path, struct stat *stbuf, struct fuse_fil
static int journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags) {
- printf("readdir called for: %s\n", path);
if (strcmp(path, "/") != 0)
return -ENOENT;
@@ -92,7 +187,6 @@ static int journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
for (size_t i = 0; i < entry_count; i++) {
char entry_name[256];
snprintf(entry_name, sizeof(entry_name), "%s.txt", entries[i].filename);
- printf("readdir exposing: %s\n", entry_name);
filler(buf, entry_name, NULL, 0, 0);
}
@@ -100,7 +194,6 @@ static int journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
}
static int journal_open(const char *path, struct fuse_file_info *fi) {
- printf("open called for: %s\n", path);
for (size_t i = 0; i < entry_count; i++) {
char entry_path[256];
snprintf(entry_path, sizeof(entry_path), "/%s.txt", entries[i].filename);
@@ -113,25 +206,40 @@ static int journal_open(const char *path, struct fuse_file_info *fi) {
static int journal_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
- printf("read called for: %s (offset: %ld, size: %zu)\n", path, offset, size);
for (size_t i = 0; i < entry_count; i++) {
char entry_path[256];
snprintf(entry_path, sizeof(entry_path), "/%s.txt", entries[i].filename);
if (strcmp(path, entry_path) == 0) {
- const char *summary = entries[i].summary;
- size_t len = strlen(summary);
+ // Concatenate SUMMARY and DESCRIPTION with proper handling
+ size_t summary_len = strlen(entries[i].summary);
+ size_t description_len = strlen(entries[i].description);
+ size_t total_len = summary_len + description_len + 2; // +1 for newline, +1 for null-terminator
+
+ char *full_content = malloc(total_len + 1); // Allocate space for the entire string
+ if (!full_content) {
+ perror("Memory allocation failed for full content");
+ return -ENOMEM;
+ }
+
+ // Copy the summary and description, add a newline between them
+ snprintf(full_content, total_len + 1, "%s\n%s", entries[i].summary, entries[i].description);
+
+ // Handle the offset
+ size_t len = strlen(full_content);
+ size_t to_copy = (offset + size > len) ? len - offset : size;
+
if (offset < len) {
- if (offset + size > len)
- size = len - offset;
- memcpy(buf, summary + offset, size);
- } else
- size = 0;
- return size;
+ memcpy(buf, full_content + offset, to_copy);
+ } else {
+ to_copy = 0; // Nothing to read if offset is beyond the length of the file
+ }
+
+ free(full_content);
+ return to_copy;
}
}
return -ENOENT;
}
-
static const struct fuse_operations journal_oper = {
.getattr = journal_getattr,
.readdir = journal_readdir,
@@ -140,8 +248,11 @@ static const struct fuse_operations journal_oper = {
};
int main(int argc, char *argv[]) {
- printf("Starting journalfs...\n");
load_journal_entries();
- return fuse_main(argc, argv, &journal_oper, NULL);
-}
+ int ret = fuse_main(argc, argv, &journal_oper, NULL);
+ // Cleanup before exit
+ free_all_entries();
+
+ return ret;
+}
diff --git a/sample_ics/2025-01-03.ics b/sample_ics/2025-01-03.ics
@@ -0,0 +1,9 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VJOURNAL
+UID:20250101T120000Z-001@host.com
+DTSTAMP:20250101T120000Z
+SUMMARY:New Year's Day Reflections
+DESCRIPTION:1. Staff meeting: Participants include Joe\, Lisa\, and Bob. Aurora project plans were reviewed.\n There is currently no budget reserves for this project. Lisa will escalate to management. Next meeting on Tuesday.\n2. Telephone Conference: ABC Corp. sales representative called to discuss new printer. Promised to get us a demo by Friday.\n3. Henry Miller (Handsoff Insurance): Car was totaled by tree. Is looking into a loaner car. 555-2323 (tel).
+END:VJOURNAL
+END:VCALENDAR