commit 77592cdf2435b0b20728556a31936ae0d7c973e0
parent 4da97e90728f15da456179f8a25c5979b54ca791
Author: Andreas Gruhler <agruhl@gmx.ch>
Date: Sun, 31 Oct 2021 00:37:48 +0200
Merge pull request #77 from in0rdr/feature/ics-import
Import ICS files
Diffstat:
13 files changed, 311 insertions(+), 101 deletions(-)
diff --git a/.obs/workflows.yml b/.obs/workflows.yml
@@ -1,6 +1,6 @@
workflow:
steps:
- - branch_package:
+ - rebuild_package:
source_project: home:in0rdr
source_package: diary-nightly
filters:
diff --git a/Makefile b/Makefile
@@ -1,6 +1,6 @@
TARGET = diary
SRCDIR = src/
-_SRC = utils.c caldav.c diary.c
+_SRC = import.c utils.c caldav.c diary.c
SRC = $(addprefix $(SRCDIR), $(_SRC))
PREFIX ?= /usr/local
BINDIR ?= $(DESTDIR)$(PREFIX)/bin
@@ -32,6 +32,9 @@ default: $(TARGET)
$(TARGET): $(SRC)
$(CC) $(SRC) -o $(TARGET) $(CFLAGS) $(LIBS)
+debug: $(SRC)
+ $(CC) $(SRC) -o $(TARGET) $(CFLAGS) -g $(LIBS)
+
clean:
rm -f $(TARGET)
diff --git a/README.md b/README.md
@@ -33,6 +33,7 @@ This is a text-based diary, inspired by [khal](https://github.com/pimutils/khal)
d, x delete current entry
s sync current entry with CalDAV server (ALPHA)
S sync all entries with CalDAV server (ALPHA)
+ i import entries from .ics file (ALPHA)
t jump to today
f jump to or find specific day
diff --git a/TESTING.md b/TESTING.md
@@ -2,6 +2,22 @@
This file holds notes for testing purposes.
+## Valgrind
+
+Use Valgrind on a build with debug symbols to discover memory issues:
+```bash
+mkdir -p tmp-journal
+make debug
+valgrind --leak-check=full ./diary tmp-journal/ 2>log.txt
+```
+
+## Compile with Debug Symbols
+
+To make a build with debug symbols use the `debug` target:
+```bash
+make debug
+```
+
## Send stderr to File
Send stderr to a file for debugging:
diff --git a/man1/diary.1 b/man1/diary.1
@@ -54,6 +54,7 @@ e, Enter | edit current entry
d, x | delete current entry
s | sync current entry with CalDAV server (ALPHA)
S | sync all entries with CalDAV server (ALPHA)
+i | import entries from .ics file (ALPHA)
t | jump to today
f | jump to or find specific day
diff --git a/src/caldav.c b/src/caldav.c
@@ -587,25 +587,6 @@ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar
}
}
-void* show_progress(void* vargp){
- WINDOW* header = (WINDOW*) vargp;
- mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 11, " syncing ");
- for(;;) {
- mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "|");
- wrefresh(header);
- usleep(200000);
- mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "/");
- wrefresh(header);
- usleep(200000);
- mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "-");
- wrefresh(header);
- usleep(200000);
- mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "\\");
- wrefresh(header);
- usleep(200000);
- }
-}
-
/*
* Sync with CalDAV server.
* Returns the answer char of the confirmation dialogue
@@ -664,7 +645,7 @@ int caldav_sync(struct tm* date,
char* tokenfile_path = expand_path(CONFIG.google_tokenfile);
if (user_principal == NULL) {
- fprintf(stderr, "Unable to fetch principal due to invalid tokenfile. Removing tokenfile '%s'.\n", CONFIG.google_tokenfile);
+ fprintf(stderr, "Unable to fetch principal. Offline or invalid tokenfile. Removing tokenfile '%s'.\n", CONFIG.google_tokenfile);
wclear(header);
mvwprintw(header, 0, 0, "Invalid Google OAuth2 credentials, removing tokenfile at '%s'. Please retry.", CONFIG.google_tokenfile);
@@ -753,7 +734,9 @@ int caldav_sync(struct tm* date,
}
struct tm* localfile_time = gmtime(&attr.st_mtime);
fprintf(stderr, "Local dst: %i\n", localfile_time->tm_isdst);
- //local_time->tm_isdst = -1;
+ // set to negative value, so mktime uses timezone information and system databases
+ // to attempt to determine whether DST is in effect at the specified time
+ localfile_time->tm_isdst = -1;
time_t localfile_date = mktime(localfile_time);
fprintf(stderr, "Local dst: %i\n", localfile_time->tm_isdst);
fprintf(stderr, "Local file last modified time: %s\n", ctime(&localfile_date));
@@ -761,21 +744,24 @@ int caldav_sync(struct tm* date,
struct tm remote_datetime;
time_t remote_date;
+ long search_pos = 0;
// check remote LAST-MODIFIED:20210521T212441Z of remote event
- char* remote_last_mod = extract_ical_field(event, "LAST-MODIFIED", false);
- char* remote_uid = extract_ical_field(event, "UID", false);
- fprintf(stderr, "Remote last modified: %s\n", remote_last_mod);
- fprintf(stderr, "Remote UID: %s\n", remote_uid);
- if (remote_last_mod == NULL) {
+ char* remote_last_mod = extract_ical_field(event, "LAST-MODIFIED", &search_pos, false);
+ char* remote_uid = extract_ical_field(event, "UID", &search_pos, false);
+
+ if (remote_last_mod == NULL || remote_uid == NULL) {
remote_file_exists = false;
} else {
+ fprintf(stderr, "Remote last modified: %s\n", remote_last_mod);
+ fprintf(stderr, "Remote UID: %s\n", remote_uid);
strptime(remote_last_mod, "%Y%m%dT%H%M%SZ", &remote_datetime);
//remote_datetime.tm_isdst = -1;
fprintf(stderr, "Remote dst: %i\n", remote_datetime.tm_isdst);
remote_date = mktime(&remote_datetime);
fprintf(stderr, "Remote dst: %i\n", remote_datetime.tm_isdst);
fprintf(stderr, "Remote last modified: %s\n", ctime(&remote_date));
+ free(remote_last_mod);
}
if (! (local_file_exists || remote_file_exists)) {
@@ -788,7 +774,7 @@ int caldav_sync(struct tm* date,
double timediff = difftime(localfile_date, remote_date);
fprintf(stderr, "Time diff between local and remote mod time:%e\n", timediff);
- if ((timediff > 0 && local_file_exists) || (local_file_exists && !remote_file_exists)) {
+ if (local_file_exists && (timediff > 0 || !remote_file_exists)) {
// local time > remote time
// if local file mod time more recent than LAST-MODIFIED
@@ -811,8 +797,8 @@ int caldav_sync(struct tm* date,
char* rmt_desc;
char dstr[16];
int conf_ch;
- if ((timediff < 0 && remote_file_exists) || (!local_file_exists && remote_file_exists)) {
- rmt_desc = extract_ical_field(event, "DESCRIPTION", true);
+ if (remote_file_exists && (timediff < 0 || !local_file_exists)) {
+ rmt_desc = extract_ical_field(event, "DESCRIPTION", &search_pos, true);
fprintf(stderr, "Remote event description:%s\n", rmt_desc);
if (rmt_desc == NULL) {
@@ -832,7 +818,7 @@ int caldav_sync(struct tm* date,
// ask for confirmation
strftime(dstr, sizeof dstr, CONFIG.fmt, date);
- mvwprintw(header, 0, 0, "Remote event is more recent. Sync entry '%s' and overwrite local file? [(Y)es/(a)all/(n)o/(c)ancel] ", dstr);
+ mvwprintw(header, 0, 0, "Remote event is more recent. Sync entry '%s' and overwrite local file? [(Y)es/(a)ll/(n)o/(c)ancel] ", dstr);
char* i;
bool conf = false;
while (!conf) {
diff --git a/src/caldav.h b/src/caldav.h
@@ -6,7 +6,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/stat.h>
diff --git a/src/diary.c b/src/diary.c
@@ -77,40 +77,6 @@ void draw_calendar(WINDOW* number_pad, WINDOW* month_pad, const char* diary_dir,
}
}
-bool go_to(WINDOW* calendar, WINDOW* aside, time_t date, int* cur_pad_pos) {
- if (date < mktime(&cal_start) || date > mktime(&cal_end))
- return false;
-
- int diff_seconds = date - mktime(&cal_start);
- int diff_days = diff_seconds / 60 / 60 / 24;
- int diff_weeks = diff_days / 7;
- int diff_wdays = diff_days % 7;
-
- localtime_r(&date, &curs_date);
-
- getyx(calendar, cy, cx);
-
- // remove the STANDOUT attribute from the day we are leaving
- chtype current_attrs = mvwinch(calendar, cy, cx) & A_ATTRIBUTES;
- // leave every attr as is, but turn off STANDOUT
- current_attrs &= ~A_STANDOUT;
- mvwchgat(calendar, cy, cx, 2, current_attrs, 0, NULL);
-
- // add the STANDOUT attribute to the day we are entering
- chtype new_attrs = mvwinch(calendar, diff_weeks, diff_wdays * 3) & A_ATTRIBUTES;
- new_attrs |= A_STANDOUT;
- mvwchgat(calendar, diff_weeks, diff_wdays * 3, 2, new_attrs, 0, NULL);
-
- if (diff_weeks < *cur_pad_pos)
- *cur_pad_pos = diff_weeks;
- if (diff_weeks > *cur_pad_pos + LINES - 2)
- *cur_pad_pos = diff_weeks - LINES + 2;
- prefresh(aside, *cur_pad_pos, 0, 1, 0, LINES - 1, ASIDE_WIDTH);
- prefresh(calendar, *cur_pad_pos, 0, 1, ASIDE_WIDTH, LINES - 1, ASIDE_WIDTH + CAL_WIDTH);
-
- return true;
-}
-
/* Update window 'win' with diary entry from date 'date' */
void display_entry(const char* dir, size_t dir_size, const struct tm* date, WINDOW* win, int width) {
char path[100];
@@ -405,6 +371,7 @@ int main(int argc, char** argv) {
return 0;
break;
case 'd':
+ free(CONFIG.dir);
// set diary directory from option character
CONFIG.dir = (char *) calloc(strlen(optarg) + 1, sizeof(char));
strcpy(CONFIG.dir, optarg);
@@ -434,6 +401,7 @@ int main(int argc, char** argv) {
}
if (optind < argc) {
+ free(CONFIG.dir);
// set diary directory from first non-option argv-element,
// required for backwarad compatibility with diary <= 0.4
CONFIG.dir = (char *) calloc(strlen(argv[optind]) + 1, sizeof(char));
@@ -474,12 +442,14 @@ int main(int argc, char** argv) {
int ch, conf_ch;
int pad_pos = 0;
int syear = 0, smonth = 0, sday = 0;
+ char ics_input_filepath[256];
+ char* expanded_ics_input_filepath;
struct tm new_date;
int prev_width = COLS - ASIDE_WIDTH - CAL_WIDTH;
int prev_height = LINES - 1;
size_t diary_dir_size = strlen(CONFIG.dir);
- bool mv_valid = go_to(cal, aside, raw_time, &pad_pos);
+ bool mv_valid = go_to(cal, aside, raw_time, &pad_pos, &curs_date, &cal_start, &cal_end);
// mark current day
atrs = winch(cal) & A_ATTRIBUTES;
wchgat(cal, 2, atrs | A_UNDERLINE, 0, NULL);
@@ -509,32 +479,32 @@ int main(int argc, char** argv) {
case 'j':
case KEY_DOWN:
new_date.tm_mday += 7;
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
case 'k':
case KEY_UP:
new_date.tm_mday -= 7;
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
case 'l':
case KEY_RIGHT:
new_date.tm_mday++;
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
case 'h':
case KEY_LEFT:
new_date.tm_mday--;
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
// jump to top/bottom of page
case 'g':
new_date = find_closest_entry(cal_start, false, CONFIG.dir, diary_dir_size);
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
case 'G':
new_date = find_closest_entry(cal_end, true, CONFIG.dir, diary_dir_size);
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
// jump backward/forward by a month
@@ -542,12 +512,12 @@ int main(int argc, char** argv) {
if (new_date.tm_mday == 1)
new_date.tm_mon--;
new_date.tm_mday = 1;
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
case 'J':
new_date.tm_mon++;
new_date.tm_mday = 1;
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
// find specific date
@@ -561,14 +531,14 @@ int main(int argc, char** argv) {
// struct tm.tm_mon in range [0, 11]
new_date.tm_mon = smonth - 1;
new_date.tm_mday = sday;
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
}
curs_set(0);
break;
// today shortcut
case 't':
new_date = today;
- mv_valid = go_to(cal, aside, raw_time, &pad_pos);
+ mv_valid = go_to(cal, aside, raw_time, &pad_pos, &curs_date, &cal_start, &cal_end);
break;
// delete entry
case 'd':
@@ -635,12 +605,12 @@ int main(int argc, char** argv) {
// Move to the previous diary entry
case 'N':
new_date = find_closest_entry(new_date, true, CONFIG.dir, diary_dir_size);
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
// Move to the next diary entry
case 'n':
new_date = find_closest_entry(new_date, false, CONFIG.dir, diary_dir_size);
- mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
+ mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos, &curs_date, &cal_start, &cal_end);
break;
// Sync entry with CalDAV server.
// Show confirmation dialogue before overwriting local files
@@ -666,6 +636,21 @@ int main(int argc, char** argv) {
it.tm_mday++;
}
break;
+ // import from ics file
+ case 'i':
+ wclear(header);
+ curs_set(2);
+ mvwprintw(header, 0, 0, "Import from file: ");
+ if (wscanw(header, "%s", &ics_input_filepath) == 1) {
+ // fprintf(stderr, "ICS input file: %s\n", ics_input_filepath);
+ expanded_ics_input_filepath = expand_path(ics_input_filepath);
+ ics_import(expanded_ics_input_filepath, header, cal, aside, &pad_pos, &curs_date, &cal_start, &cal_end);
+ free(expanded_ics_input_filepath);
+ }
+
+ curs_set(0);
+ echo();
+ break;
}
if (mv_valid) {
@@ -682,6 +667,7 @@ int main(int argc, char** argv) {
} while (ch != 'q');
free(config_file_path);
+ free(CONFIG.dir);
endwin();
system("clear");
return 0;
diff --git a/src/diary.h b/src/diary.h
@@ -17,6 +17,7 @@
#include <locale.h>
#include <langinfo.h>
#include "utils.h"
+#include "import.h"
#include "caldav.h"
#define XDG_CONFIG_HOME_FALLBACK "~/.config"
@@ -29,7 +30,6 @@ void setup_cal_timeframe();
void draw_wdays(WINDOW* head);
void draw_calendar(WINDOW* number_pad, WINDOW* month_pad, const char* diary_dir, size_t diary_dir_size);
-bool go_to(WINDOW* calendar, WINDOW* aside, time_t date, int* cur_pad_pos);
void display_entry(const char* dir, size_t dir_size, const struct tm* date, WINDOW* win, int width);
void edit_cmd(const char* dir, size_t dir_size, const struct tm* date, char** rcmd, size_t rcmd_size);
diff --git a/src/import.c b/src/import.c
@@ -0,0 +1,118 @@
+#include "import.h"
+
+/* Import journal entries from an ics file */
+void ics_import(const char* ics_input, WINDOW* header, WINDOW* cal, WINDOW* aside, int* pad_pos, struct tm* curs_date, struct tm* cal_start, struct tm* cal_end) {
+ pthread_t progress_tid;
+
+ FILE* pfile = fopen(ics_input, "r");
+ if (pfile == NULL) {
+ perror("Error opening file");
+ return;
+ }
+
+ fseek(pfile, 0, SEEK_END);
+ long ics_bytes = ftell(pfile);
+ rewind(pfile);
+
+ char* ics = malloc(ics_bytes + 1);
+ fread(ics, 1, ics_bytes, pfile);
+ fclose(pfile);
+
+ ics[ics_bytes] = 0;
+ // fprintf(stderr, "Import ICS file: %s\n", ics);
+
+ int conf_ch = 0;
+ char dstr[16];
+ struct tm date = {};
+
+ long search_pos = 0;
+ char* vevent;
+ char* vevent_date;
+ char* vevent_desc;
+
+ // find all VEVENTs and write to files
+ char *i = ics;
+ while (i < ics + ics_bytes) {
+ vevent = extract_ical_field(i, "BEGIN:VEVENT", &search_pos, false);
+ vevent_date = extract_ical_field(i, "DTSTART", &search_pos, false);
+ vevent_desc = extract_ical_field(i, "DESCRIPTION", &search_pos, true);
+ if (vevent == NULL || vevent_desc == NULL) {
+ break;
+ }
+
+ i += search_pos;
+
+ // fprintf(stderr, "VEVENT DESCRIPTION: \n\n%s\n\n", vevent_desc);
+
+ // parse date
+ strptime(vevent_date, "%Y%m%d", &date);
+ strftime(dstr, sizeof dstr, CONFIG.fmt, &date);
+
+ // get path of entry
+ char path[100];
+ char* ppath = path;
+ fpath(CONFIG.dir, strlen(CONFIG.dir), &date, &ppath, sizeof path);
+ fprintf(stderr, "Import date file path: %s\n", path);
+
+ if (conf_ch == 'c') {
+ // cancel all
+ break;
+ }
+
+ if (conf_ch != 'a') {
+ // prepare header for confirmation dialogue
+ curs_set(2);
+ noecho();
+ wclear(header);
+
+ // ask for confirmation
+ mvwprintw(header, 0, 0, "Import entry '%s' and overwrite local file? [(Y)es/(a)ll/(n)o/(c)ancel] ", dstr);
+ conf_ch = wgetch(header);
+ }
+
+ if (conf_ch == 'y' || conf_ch == 'Y' || conf_ch == 'a' || conf_ch == '\n') {
+ pthread_create(&progress_tid, NULL, show_progress, (void*)header);
+
+ // persist VEVENT to local file
+ FILE* cursordate_file = fopen(path, "wb");
+ if (cursordate_file == NULL) {
+ perror("Failed to open import date file");
+ } else {
+ for (char* j = vevent_desc; *j != '\0'; j++) {
+ if (vevent_desc[j-vevent_desc] == 0x5C) { // backslash
+ switch (*(j+1)) {
+ case 'n':
+ fputc('\n', cursordate_file);
+ j++;
+ break;
+ case 0x5c: // preserve real backslash
+ fputc(0x5c, cursordate_file);
+ j++;
+ break;
+ }
+ } else {
+ fputc(*j, cursordate_file);
+ }
+ }
+ fclose(cursordate_file);
+
+ bool mv_valid = go_to(cal, aside, mktime(&date), pad_pos, curs_date, cal_start, cal_end);
+ if (mv_valid) {
+ // add new entry highlight
+ chtype atrs = winch(cal) & A_ATTRIBUTES;
+ wchgat(cal, 2, atrs | A_BOLD, 0, NULL);
+ prefresh(cal, *pad_pos, 0, 1, ASIDE_WIDTH, LINES - 1, ASIDE_WIDTH + CAL_WIDTH);
+ }
+ pthread_cancel(progress_tid);
+ }
+ }
+
+ // fprintf(stderr, "Import DTSTART: %s\n", desc);
+ // fprintf(stderr, "Import DESCRIPTION: %s\n", desc);
+ fprintf(stderr, "* * * * * * * * * * * * * \n");
+ }
+ free(vevent);
+ free(vevent_date);
+ free(vevent_desc);
+ free(ics);
+}
+\ No newline at end of file
diff --git a/src/import.h b/src/import.h
@@ -0,0 +1,16 @@
+#ifndef DIARY_IMPORT_H
+#define DIARY_IMPORT_H
+
+#define __USE_XOPEN
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <time.h>
+#include <pthread.h>
+#include "utils.h"
+
+void ics_import(const char* ics_input, WINDOW* header, WINDOW* cal, WINDOW* aside, int* pad_pos, struct tm* curs_date, struct tm* cal_start, struct tm* cal_end);
+
+#endif
+\ No newline at end of file
diff --git a/src/utils.c b/src/utils.c
@@ -154,10 +154,6 @@ char* fold(const char* str) {
char* unfold(const char* str) {
fprintf(stderr, "Before unfolding: %s\n", str);
- //if (strcmp(str, "")) {
- // fputs("Unfold string is empty.\n", stderr);
- // return NULL;
- //}
// work on a copy of the str
char* strcp = (char *) malloc(strlen(str) * sizeof(char) + 1);
@@ -169,6 +165,12 @@ char* unfold(const char* str) {
char* res = strtok(strcp, "\n");
+ if (res == NULL) {
+ fprintf(stderr, "No more lines in multiline string, stop unfolding.\n");
+ free(strcp);
+ return NULL;
+ }
+
char* buf = malloc(strlen(res) + 1);
if (buf == NULL) {
perror("malloc failed");
@@ -188,6 +190,11 @@ char* unfold(const char* str) {
while (res != NULL) {
res = strtok(NULL, "\n");
+ if (res == NULL) {
+ fprintf(stderr, "No more lines in multiline string, stop unfolding.\n");
+ break;
+ }
+
if (regexec(&re, res, 1, pm, 0) == 0) {
// Stop unfolding if line does not start with white space/tab:
// https://datatracker.ietf.org/doc/html/rfc2445#section-4.1
@@ -195,10 +202,9 @@ char* unfold(const char* str) {
}
newbuf = realloc(buf, strlen(buf) + strlen(res) + 1);
- if (buf == NULL) {
+ if (newbuf == NULL) {
perror("realloc failed");
- free(buf);
- return NULL;
+ break;
} else {
buf = newbuf;
strcat(buf, res + 1);
@@ -210,7 +216,10 @@ char* unfold(const char* str) {
return buf;
}
-char* extract_ical_field(const char* ics, char* key, bool multiline) {
+/* Find ical key in string. The value of 'start_pos' is set to the start position
+ of the value (match) after the colon (':').
+*/
+char* extract_ical_field(const char* ics, char* key, long* start_pos, bool multiline) {
regex_t re;
regmatch_t pm[1];
char key_regex[strlen(key) + 1];
@@ -223,7 +232,7 @@ char* extract_ical_field(const char* ics, char* key, bool multiline) {
}
// work on a copy of the ical xml response
- char* icscp = (char *) malloc(strlen(ics) * sizeof(char) + 1);
+ char* icscp = (char *) malloc(strlen(ics) + 1 * sizeof(char));
if (icscp == NULL) {
perror("malloc failed");
return NULL;
@@ -231,31 +240,43 @@ char* extract_ical_field(const char* ics, char* key, bool multiline) {
strcpy(icscp, ics);
// tokenize ical by newlines
+ char* buf = NULL;
char* res = strtok(icscp, "\n");
-
while (res != NULL) {
if (regexec(&re, res, 1, pm, 0) == 0) {
+ // found the key in line 'res'
res = strstr(res, ":"); // value
res++; // strip the ":"
fprintf(stderr, "Extracted ical result value: %s\n", res);
fprintf(stderr, "Extracted ical result size: %li\n", strlen(res));
- if (strlen(res) == 0) {
- // empty remote description
- res = NULL;
- } else if (multiline) {
- res = unfold(ics + (res - icscp));
+ fprintf(stderr, "Sizeof ics: %li\n", strlen(ics));
+ fprintf(stderr, "Start pos: %li\n", *start_pos);
+ fprintf(stderr, "Res: %s\n", res);
+ *start_pos = res - icscp;
+ fprintf(stderr, "Start pos: %li\n", *start_pos);
+
+ if (strlen(res) != 0) {
+ // non-empty remote value
+ if (multiline) {
+ buf = unfold(ics + *start_pos);
+ } else {
+ buf = malloc(strlen(res) + 1);
+ if (buf == NULL) {
+ perror("malloc failed");
+ return NULL;
+ }
+ strcpy(buf, res);
+ }
}
- break;
}
- // key not in this line, advance line
res = strtok(NULL, "\n");
}
- fprintf(stderr, "Sizeof ics: %li\n", strlen(ics));
+ regfree(&re);
free(icscp);
- return res;
+ return buf;
}
// Return expanded file path
@@ -319,7 +340,65 @@ void fpath(const char* dir, size_t dir_size, const struct tm* date, char** rpath
strcat(*rpath, dstr);
}
+bool go_to(WINDOW* calendar, WINDOW* aside, time_t date, int* cur_pad_pos, struct tm* curs_date, struct tm* cal_start, struct tm* cal_end) {
+ if (date < mktime(cal_start) || date > mktime(cal_end)) {
+ fprintf(stderr, "Invalid cursor move, return from go_to\n");
+ return false;
+ }
+
+ int diff_seconds = date - mktime(cal_start);
+ int diff_days = diff_seconds / 60 / 60 / 24;
+ int diff_weeks = diff_days / 7;
+ int diff_wdays = diff_days % 7;
+
+ localtime_r(&date, curs_date);
+
+ int cy, cx;
+ getyx(calendar, cy, cx);
+
+ // remove the STANDOUT attribute from the day we are leaving
+ chtype current_attrs = mvwinch(calendar, cy, cx) & A_ATTRIBUTES;
+ // leave every attr as is, but turn off STANDOUT
+ current_attrs &= ~A_STANDOUT;
+ mvwchgat(calendar, cy, cx, 2, current_attrs, 0, NULL);
+
+ // add the STANDOUT attribute to the day we are entering
+ chtype new_attrs = mvwinch(calendar, diff_weeks, diff_wdays * 3) & A_ATTRIBUTES;
+ new_attrs |= A_STANDOUT;
+ mvwchgat(calendar, diff_weeks, diff_wdays * 3, 2, new_attrs, 0, NULL);
+
+ if (diff_weeks < *cur_pad_pos)
+ *cur_pad_pos = diff_weeks;
+ if (diff_weeks > *cur_pad_pos + LINES - 2)
+ *cur_pad_pos = diff_weeks - LINES + 2;
+ prefresh(aside, *cur_pad_pos, 0, 1, 0, LINES - 1, ASIDE_WIDTH);
+ prefresh(calendar, *cur_pad_pos, 0, 1, ASIDE_WIDTH, LINES - 1, ASIDE_WIDTH + CAL_WIDTH);
+
+ return true;
+}
+
+
+void* show_progress(void* vargp){
+ WINDOW* header = (WINDOW*) vargp;
+ mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 11, " syncing ");
+ for(;;) {
+ mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "|");
+ wrefresh(header);
+ usleep(200000);
+ mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "/");
+ wrefresh(header);
+ usleep(200000);
+ mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "-");
+ wrefresh(header);
+ usleep(200000);
+ mvwprintw(header, 0, COLS - CAL_WIDTH - ASIDE_WIDTH - 10, "\\");
+ wrefresh(header);
+ usleep(200000);
+ }
+}
+
config CONFIG = {
+ .dir = NULL,
.range = 1,
.weekday = 1,
.fmt = "%Y-%m-%d",
diff --git a/src/utils.h b/src/utils.h
@@ -3,6 +3,7 @@
#include <regex.h>
#include <stdio.h>
+#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
@@ -26,10 +27,12 @@ void update_date(WINDOW* header, struct tm* curs_date);
char* extract_json_value(const char* json, char* key, bool quoted);
char* fold(const char* str);
char* unfold(const char* str);
-char* extract_ical_field(const char* ical, char* key, bool multline);
+char* extract_ical_field(const char* ical, char* key, long* start_pos, bool multline);
char* expand_path(const char* str);
char* strrstr(char *haystack, char *needle);
void fpath(const char* dir, size_t dir_size, const struct tm* date, char** rpath, size_t rpath_size);
+bool go_to(WINDOW* calendar, WINDOW* aside, time_t date, int* cur_pad_pos, struct tm* curs_date, struct tm* cal_start, struct tm* cal_end);
+void* show_progress(void* vargp);
typedef struct
{