195 lines
4.8 KiB
C
195 lines
4.8 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#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;
|
||
|
}
|