--- /dev/null
+/*
+ * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _POSIX_C_SOURCE 200112L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "conf.h"
+#include "date.h"
+#include "cal.h"
+#include "daemon.h"
+
+/* Config data */
+static int reminder = 15*60;
+static char *command = NULL;
+
+/* Local data */
+static date_t old;
+static date_t new;
+
+/* Local functions */
+static void date_str(char *str, size_t size, date_t *date)
+{
+ snprintf(str, size, "%04d-%02d-%02d %02d:%02d:%02d",
+ date->year+1, date->month+1, date->day,
+ date->hour, date->min, date->sec);
+}
+
+static void notify(event_t *event)
+{
+ static char start_str[32];
+ static char end_str[32];
+
+ date_t start = event->start;
+ date_t end = event->end;
+ wday_t wday = day_of_week(start.year, start.month, start.day);
+
+ if (command) {
+ date_str(start_str, sizeof(start_str), &event->start);
+ date_str(end_str, sizeof(end_str), &event->end);
+
+ setenv("EVENT_NAME", event->name ?: "", 1);
+ setenv("EVENT_DESC", event->desc ?: "", 1);
+ setenv("EVENT_LOC", event->loc ?: "", 1);
+ setenv("EVENT_START", start_str, 1);
+ setenv("EVENT_END", end_str, 1);
+
+ system(command);
+
+ unsetenv("EVENT_NAME");
+ unsetenv("EVENT_DESC");
+ unsetenv("EVENT_LOC");
+ unsetenv("EVENT_START");
+ unsetenv("EVENT_END");
+ } else {
+ printf("%s, %s %d, %d, %02d:%02d-%02d:%02d\n",
+ day_to_string(wday),
+ month_to_string(start.month),
+ start.day+1,
+ start.year,
+ start.hour, start.min,
+ end.hour, end.min);
+
+ if (event->name)
+ printf(" Name: %s\n", event->name);
+ if (event->loc)
+ printf(" Location: %s\n", event->loc);
+ if (event->desc)
+ printf(" Details: %s\n", event->desc);
+ }
+}
+
+static int step(void)
+{
+ int delay = 24*60*60;
+
+ debug("Polling: %02dh %02dm %02ds",
+ NOW.hour, NOW.min, NOW.sec);
+
+ /* Bump timestamps */
+ old = new;
+ new = NOW;
+
+ /* Print notifications */
+ for (event_t *cur = EVENTS; cur; cur = cur->next) {
+ if (compare(&cur->start, &old) < 0)
+ continue;
+
+ int old_until = get_secs(&old, &cur->start);
+ int new_until = get_secs(&new, &cur->start);
+
+ if (old_until > reminder && new_until <= reminder) {
+ debug("Notify: %s", cur->name);
+ notify(cur);
+ }
+ if (new_until > reminder) {
+ delay = new_until - reminder;
+ break;
+ }
+ }
+
+ /* Return delay */
+ return delay;
+}
+
+/* Initialize */
+void daemon_init(void)
+{
+ old = NOW;
+ new = NOW;
+}
+
+void daemon_main(void)
+{
+ int delay = 0;
+ while (1) {
+ date_sync();
+ delay = step();
+ fflush(stdout);
+ sleep(delay);
+ }
+}
+
+void daemon_exit(void)
+{
+}
+
+/* Config parser */
+void daemon_config(const char *group, const char *name, const char *key, const char *value)
+{
+ if (match(group, "daemon")) {
+ if (match(key, "reminder")) {
+ reminder = get_number(value);
+ } else if (match(key, "notify")) {
+ command = get_string(value);
+ }
+ }
+}
+
+/* Test */
+void daemon_test(void)
+{
+ static event_t event0 = {
+ .name = "test event",
+ .desc = "this event is random and does not exist",
+ };
+ static event_t event1 = {
+ .name = "test event",
+ .desc = "this event is random and does not exist",
+ };
+ static todo_t todo = {
+ .name = "test todo",
+ .desc = "this todo is random and does not exist",
+ .status = 50,
+ };
+
+ EVENTS = &event0;
+ EVENTS->next = &event1;
+ TODOS = &todo;
+
+ date_sync();
+
+ NOW.hour = 0;
+ NOW.min = 0;
+ NOW.sec = 0;
+
+ event0.start = ((date_t){NOW.year, NOW.month, NOW.day, 12, 0});
+ event0.end = ((date_t){NOW.year, NOW.month, NOW.day, 13, 0});
+ event1.start = ((date_t){NOW.year, NOW.month, NOW.day, 14, 0});
+ event1.end = ((date_t){NOW.year, NOW.month, NOW.day, 15, 0});
+ todo.due = ((date_t){NOW.year, NOW.month, NOW.day, 16, 0});
+
+ util_init();
+
+ daemon_config("daemon", NULL, "reminder", "15");
+ daemon_config("daemon", NULL, "notify", "lackey-notify");
+
+ daemon_init();
+ while (NOW.day == EVENTS->start.day) {
+ int delay = step();
+ delay = MIN(delay, rand() % (60*60));
+ NOW = get_date(get_stamp(NOW)+delay);
+ }
+ daemon_exit();
+}