#include #include #include #define MAX_LINE_LENGTH 256 #define DATE_LENGTH 10 #define TIME_LENGTH 5 #define CATEGORY_LENGTH 10 #define FREETEXT_MAX_LENGTH 80 #define CNOTES_DIR ".local/share/cnotes" #define CNOTES_FILE "cnotes.csv" typedef struct { char date[DATE_LENGTH + 1]; char time[TIME_LENGTH + 1]; char category[CATEGORY_LENGTH + 1]; char freetext[FREETEXT_MAX_LENGTH + 1]; } Entry; /* ReSharper disable CppParameterMayBeConst */ void print_horizontal_line(char left, char middle, char right, char fill) { /* ReSharper restore CppParameterMayBeConst */ int i; printf("%c", left); /* Date column */ for (i = 0; i < DATE_LENGTH + 2; i++) printf("%c", fill); printf("%c", middle); /* Time column */ for (i = 0; i < TIME_LENGTH + 2; i++) printf("%c", fill); printf("%c", middle); /* Category column */ for (i = 0; i < CATEGORY_LENGTH + 2; i++) printf("%c", fill); printf("%c", middle); /* Free text column */ for (i = 0; i < FREETEXT_MAX_LENGTH + 2; i++) printf("%c", fill); printf("%c\n", right); } void print_header(void) { /* Top border (double line) */ print_horizontal_line('+', '+', '+', '='); /* Header row */ printf("| %-*s | %-*s | %-*s | %-*s |\n", DATE_LENGTH, "Date", TIME_LENGTH, "Time", CATEGORY_LENGTH, "Category", FREETEXT_MAX_LENGTH, "Free Text"); /* Bottom border (double line) */ print_horizontal_line('+', '+', '+', '='); } void print_entry(const Entry *entry) { printf("| %-*s | %-*s | %-*s | %-*s |\n", DATE_LENGTH, entry->date, TIME_LENGTH, entry->time, CATEGORY_LENGTH, entry->category, FREETEXT_MAX_LENGTH, entry->freetext); } void print_footer(void) { print_horizontal_line('+', '+', '+', '-'); } int parse_line(const char *line, Entry *entry) { const char *ptr; int i; ptr = line; /* Parse date (10 chars) */ if (strlen(ptr) < DATE_LENGTH) return 0; strncpy(entry->date, ptr, DATE_LENGTH); entry->date[DATE_LENGTH] = '\0'; ptr += DATE_LENGTH; /* Skip comma */ if (*ptr != ',') return 0; ptr++; /* Parse time (5 chars) */ if (strlen(ptr) < TIME_LENGTH) return 0; strncpy(entry->time, ptr, TIME_LENGTH); entry->time[TIME_LENGTH] = '\0'; ptr += TIME_LENGTH; /* Skip comma */ if (*ptr != ',') return 0; ptr++; /* Parse category (10 chars) */ if (strlen(ptr) < CATEGORY_LENGTH) return 0; strncpy(entry->category, ptr, CATEGORY_LENGTH); entry->category[CATEGORY_LENGTH] = '\0'; ptr += CATEGORY_LENGTH; /* Skip comma */ if (*ptr != ',') return 0; ptr++; /* Parse free text (quoted) */ if (*ptr != '"') return 0; ptr++; i = 0; while (*ptr != '\0' && *ptr != '"' && i < FREETEXT_MAX_LENGTH) { entry->freetext[i++] = *ptr++; } entry->freetext[i] = '\0'; /* Should end with quote */ if (*ptr != '"') return 0; return 1; } int get_cnotes_path(char *buffer, size_t bufsize) { const char *home; size_t len; home = getenv("HOME"); if (home == NULL) { fprintf(stderr, "Error: HOME environment variable not set\n"); return 0; } /* Build path: $HOME/.local/share/cnotes/cnotes.csv */ len = strlen(home); if (len + strlen(CNOTES_DIR) + strlen(CNOTES_FILE) + 3 > bufsize) { fprintf(stderr, "Error: Path too long\n"); return 0; } sprintf(buffer, "%s/%s/%s", home, CNOTES_DIR, CNOTES_FILE); return 1; } int main(void) { FILE *file; char file_path[600]; char line[MAX_LINE_LENGTH]; Entry entry; int line_count; int first_entry; /* Get cnotes file path */ if (!get_cnotes_path(file_path, sizeof(file_path))) { return 1; } file = fopen(file_path, "r"); if (file == NULL) { fprintf(stderr, "Error: Cannot open file '%s'\n", file_path); fprintf(stderr, "Hint: Use 'addnote' to create your first entry\n"); return 1; } line_count = 0; first_entry = 1; while (fgets(line, sizeof(line), file) != NULL) { line_count++; /* Remove trailing newline */ line[strcspn(line, "\n")] = '\0'; /* Skip empty lines */ if (strlen(line) == 0) continue; if (parse_line(line, &entry)) { if (first_entry) { print_header(); first_entry = 0; } print_entry(&entry); } else { fprintf(stderr, "Warning: Skipping malformed line %d\n", line_count); } } if (!first_entry) { print_footer(); } else { printf("No valid entries found.\n"); } fclose(file); return 0; }