Compare commits

..

2 Commits

Author SHA1 Message Date
7880a25fef cleanup and prep for continued work 2025-10-12 20:32:58 +01:00
2ba44e65be dumpnotes rewrite 2025-10-12 20:22:01 +01:00
2 changed files with 171 additions and 113 deletions

View File

@ -8,7 +8,7 @@
#include "platform.h" #include "platform.h"
#define CATEGORY_LENGTH 10 #define CATEGORY_LENGTH 10
#define FREETEXT_MAX_LENGTH 80 #define FREETEXT_MAX_LENGTH 125
#define DEFAULT_CATEGORY "General" #define DEFAULT_CATEGORY "General"
#define CNOTES_DIR ".local/share/cnotes" #define CNOTES_DIR ".local/share/cnotes"
#define CNOTES_FILE "cnotes.csv" #define CNOTES_FILE "cnotes.csv"
@ -155,7 +155,6 @@ int main(int argc, char *argv[]) {
category = argv[i + 1]; category = argv[i + 1];
i += 2; i += 2;
} else { } else {
/* This is the message */
message = argv[i]; message = argv[i];
i++; i++;
break; /* Only take first non-option as message */ break; /* Only take first non-option as message */

View File

@ -1,22 +1,55 @@
//
// Created by gmgauthier on 05/10/2025.
//
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define MAX_LINE_LENGTH 256 #define MAX_LENGTH 500 /* Allow fgets to read up to this many characters */
#define DATE_LENGTH 10 #define DATE_LENGTH 10 /* YYYY-MM-DD */
#define TIME_LENGTH 5 #define TIME_LENGTH 5 /* HH:MM */
#define CATEGORY_LENGTH 10 #define CATEGORY_LENGTH 10 /* CATEGORY__ */
#define FREETEXT_MAX_LENGTH 80 #define TXTMSG_LENGTH 125 /* Max length of a text message */
#define MAX_ENTRIES 5000 /* Maximum number of entries to load */
#define CNOTES_FILE "notes.csv"
#define CNOTES_DIR ".local/share/cnotes" #define CNOTES_DIR ".local/share/cnotes"
#define CNOTES_FILE "cnotes.csv"
typedef enum {
SORT_NONE,
SORT_DATE,
SORT_CATEGORY
} SortMode;
/* Global variable to control sort order: 1 for ascending, -1 for descending */
static int g_sort_order = -1; /* Default: descending (newest/Z-A first) */
typedef struct { typedef struct {
char date[DATE_LENGTH + 1]; char date[DATE_LENGTH +1]; /* YYYY-MM-DD\0 */
char time[TIME_LENGTH + 1]; char time[TIME_LENGTH +1]; /* HH:MM\0 */
char category[CATEGORY_LENGTH + 1]; char category[CATEGORY_LENGTH +1]; /* CATEGORY__\0 */
char freetext[FREETEXT_MAX_LENGTH + 1]; char text[TXTMSG_LENGTH +1]; /* Text message\0 */
} Entry; } Entry;
/* Comparison function for sorting by date/time */
static int compare_by_date(const void *a, const void *b) {
const Entry *entry_a = (const Entry *)a;
const Entry *entry_b = (const Entry *)b;
int date_cmp = strcmp(entry_a->date, entry_b->date);
if (date_cmp != 0) return date_cmp * g_sort_order;
return strcmp(entry_a->time, entry_b->time) * g_sort_order;
}
/* Comparison function for sorting by category */
static int compare_by_category(const void *a, const void *b) {
const Entry *entry_a = (const Entry *)a;
const Entry *entry_b = (const Entry *)b;
return strcmp(entry_b->category, entry_a->category) * g_sort_order;
}
/* ReSharper disable CppParameterMayBeConst */ /* ReSharper disable CppParameterMayBeConst */
void print_horizontal_line(char left, char middle, char right, char fill) { void print_horizontal_line(char left, char middle, char right, char fill) {
/* ReSharper restore CppParameterMayBeConst */ /* ReSharper restore CppParameterMayBeConst */
@ -36,7 +69,7 @@ void print_horizontal_line(char left, char middle, char right, char fill) {
printf("%c", middle); printf("%c", middle);
/* Free text column */ /* Free text column */
for (i = 0; i < FREETEXT_MAX_LENGTH + 2; i++) printf("%c", fill); for (i = 0; i < TXTMSG_LENGTH + 2; i++) printf("%c", fill);
printf("%c\n", right); printf("%c\n", right);
} }
@ -49,72 +82,70 @@ void print_header(void) {
DATE_LENGTH, "Date", DATE_LENGTH, "Date",
TIME_LENGTH, "Time", TIME_LENGTH, "Time",
CATEGORY_LENGTH, "Category", CATEGORY_LENGTH, "Category",
FREETEXT_MAX_LENGTH, "Free Text"); TXTMSG_LENGTH, "Free Text");
/* Bottom border (double line) */ /* Bottom border (double line) */
print_horizontal_line('+', '+', '+', '='); print_horizontal_line('+', '+', '+', '=');
} }
void print_footer(void) {
print_horizontal_line('+', '+', '+', '-');
}
void print_entry(const Entry *entry) { void print_entry(const Entry *entry) {
printf("| %-*s | %-*s | %-*s | %-*s |\n", printf("| %-*s | %-*s | %-*s | %-*s |\n",
DATE_LENGTH, entry->date, DATE_LENGTH, entry->date,
TIME_LENGTH, entry->time, TIME_LENGTH, entry->time,
CATEGORY_LENGTH, entry->category, CATEGORY_LENGTH, entry->category,
FREETEXT_MAX_LENGTH, entry->freetext); TXTMSG_LENGTH, entry->text);
} }
void print_footer(void) {
print_horizontal_line('+', '+', '+', '-'); /* Parse a fixed-length field followed by a delimiter */
static const char *parse_fixed_field(const char *ptr, char *dest, int length, char delimiter) {
if (strlen(ptr) < length) return NULL;
strncpy(dest, ptr, length);
dest[length] = '\0';
ptr += length;
if (*ptr != delimiter) return NULL;
return ptr + 1; /* skip delimiter */
}
/* Parse a variable-length field up to max_length, terminated by delimiter */
/* Truncates if field exceeds max_length but continues to find delimiter */
static const char *parse_variable_field(const char *ptr, char *dest, int max_length, char delimiter) {
int i = 0;
while (*ptr != '\0' && *ptr != delimiter) {
if (i < max_length) {
dest[i++] = *ptr;
}
ptr++;
}
dest[i] = '\0';
if (*ptr != delimiter) return NULL;
return ptr + 1; /* skip delimiter */
} }
int parse_line(const char *line, Entry *entry) { int parse_line(const char *line, Entry *entry) {
const char *ptr; const char *charptr = line;
int i;
ptr = line; /* Parse date field (fixed length) */
charptr = parse_fixed_field(charptr, entry->date, DATE_LENGTH, ',');
if (!charptr) return 0;
/* Parse date (10 chars) */ /* Parse time field (fixed length) */
if (strlen(ptr) < DATE_LENGTH) return 0; charptr = parse_fixed_field(charptr, entry->time, TIME_LENGTH, ',');
strncpy(entry->date, ptr, DATE_LENGTH); if (!charptr) return 0;
entry->date[DATE_LENGTH] = '\0';
ptr += DATE_LENGTH;
/* Skip comma */ /* Parse category field (variable length) */
if (*ptr != ',') return 0; charptr = parse_variable_field(charptr, entry->category, CATEGORY_LENGTH, ',');
ptr++; if (!charptr) return 0;
/* Parse time (5 chars) */ /* Parse text field (variable length, quoted) */
if (strlen(ptr) < TIME_LENGTH) return 0; if (*charptr != '"') return 0;
strncpy(entry->time, ptr, TIME_LENGTH); charptr++;
entry->time[TIME_LENGTH] = '\0'; charptr = parse_variable_field(charptr, entry->text, TXTMSG_LENGTH, '"');
ptr += TIME_LENGTH; if (!charptr) return 0;
/* 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; return 1;
} }
@ -140,55 +171,83 @@ int get_cnotes_path(char *buffer, size_t bufsize) {
return 1; return 1;
} }
int main(void) { /* start with a simple cat of the file */
FILE *file; int main(int argc, char *argv[]) {
FILE *notesfile;
char file_path[600]; char file_path[600];
char line[MAX_LINE_LENGTH]; Entry entries[MAX_ENTRIES];
Entry entry; int entry_count = 0;
int line_count; char line[MAX_LENGTH +1];
int first_entry; char *filename = CNOTES_FILE;
SortMode sort_mode = SORT_NONE;
int i;
/* Parse command line arguments */
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--date") == 0) {
if (sort_mode != SORT_NONE) {
fprintf(stderr, "dumpnotes: cannot specify multiple sort options\n");
fprintf(stderr, "usage: dumpnotes [[-d|--date] | [-c|--category]] [-r|--reverse] [filename]\n");
exit(1);
}
sort_mode = SORT_DATE;
}
else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--category") == 0) {
if (sort_mode != SORT_NONE) {
fprintf(stderr, "dumpnotes: cannot specify multiple sort options\n");
fprintf(stderr, "usage: dumpnotes [[-d|--date] | [-c|--category]] [-r|--reverse] [filename]\n");
exit(1);
}
sort_mode = SORT_CATEGORY;
}
else if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--reverse") == 0) {
g_sort_order = 1; /* Ascending order */
}
else {
filename = argv[i];
}
}
/* Get cnotes file path */ /* Get cnotes file path */
if (!get_cnotes_path(file_path, sizeof(file_path))) { if (!get_cnotes_path(file_path, sizeof(file_path))) {
return 1; return 1;
} }
file = fopen(file_path, "r"); notesfile = fopen(file_path, "r");
if (file == NULL) { if (notesfile == NULL) {
fprintf(stderr, "Error: Cannot open file '%s'\n", file_path); fprintf(stderr, "Error: Cannot open file '%s'\n", file_path);
fprintf(stderr, "Hint: Use 'addnote' to create your first entry\n"); fprintf(stderr, "Hint: Use 'addnote' to create your first entry\n");
return 1; return 1;
} }
line_count = 0; /* Read all entries into memory */
first_entry = 1; while (fgets(line, MAX_LENGTH, notesfile) != NULL && entry_count < MAX_ENTRIES) {
if (parse_line(line, &entries[entry_count])) {
entry_count++;
}
else {
fprintf(stderr, "dumpnotes: invalid line: %s\n", line);
}
}
fclose(notesfile);
while (fgets(line, sizeof(line), file) != NULL) { /* Sort if requested */
line_count++; if (sort_mode == SORT_DATE) {
qsort(entries, entry_count, sizeof(Entry), compare_by_date);
}
else if (sort_mode == SORT_CATEGORY) {
qsort(entries, entry_count, sizeof(Entry), compare_by_category);
}
/* Remove trailing newline */ /* Print the table */
line[strcspn(line, "\n")] = '\0';
/* Skip empty lines */
if (strlen(line) == 0) continue;
if (parse_line(line, &entry)) {
if (first_entry) {
print_header(); print_header();
first_entry = 0; for (i = 0; i < entry_count; i++) {
print_entry(&entries[i]);
} }
print_entry(&entry);
} else {
fprintf(stderr, "Warning: Skipping malformed line %d\n", line_count);
}
}
if (!first_entry) {
print_footer(); print_footer();
} else {
printf("No valid entries found.\n");
}
fclose(file);
return 0; return 0;
} }