From bfe73d0e5d66c43006a8a0dd8d58e311432bcb57 Mon Sep 17 00:00:00 2001
From: Lukas Fleischer <calcurse@cryptocrack.de>
Date: Wed, 6 Aug 2014 08:53:51 +0200
Subject: Add item filters

This adds the following filter options that allow for restricting the
set of items that are read from the appointments file:

* --filter-type
* --filter-start-from
* --filter-start-to
* --filter-start-after
* --filter-start-before
* --filter-end-from
* --filter-end-to
* --filter-end-after
* --filter-end-before

Signed-off-by: Lukas Fleischer <calcurse@cryptocrack.de>
---
 src/apoint.c   |  17 +++++++-
 src/args.c     | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/calcurse.c |   4 +-
 src/calcurse.h |  29 ++++++++++---
 src/dmon.c     |   2 +-
 src/event.c    |  13 +++++-
 src/io.c       |  11 ++---
 src/recur.c    |  30 +++++++++++++-
 8 files changed, 207 insertions(+), 24 deletions(-)

(limited to 'src')

diff --git a/src/apoint.c b/src/apoint.c
index f00dd1e..9bdd575 100644
--- a/src/apoint.c
+++ b/src/apoint.c
@@ -163,7 +163,7 @@ void apoint_write(struct apoint *o, FILE * f)
 }
 
 struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
-			   char state, char *note)
+			   char state, char *note, struct item_filter *filter)
 {
 	char buf[BUFSIZ], *newline;
 	time_t tstart, tend;
@@ -193,6 +193,21 @@ struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
 	tend = mktime(&end);
 	EXIT_IF(tstart == -1 || tend == -1 || tstart > tend,
 		_("date error in appointment"));
+
+	/* Filter item. */
+	if (filter) {
+		if (!(filter->type_mask & TYPE_MASK_APPT))
+			return NULL;
+		if (filter->start_from >= 0 && tstart < filter->start_from)
+			return NULL;
+		if (filter->start_to >= 0 && tstart > filter->start_to)
+			return NULL;
+		if (filter->end_from >= 0 && tend < filter->end_from)
+			return NULL;
+		if (filter->end_to >= 0 && tend > filter->end_to)
+			return NULL;
+	}
+
 	return apoint_new(buf, note, tstart, tend - tstart, state);
 }
 
diff --git a/src/args.c b/src/args.c
index 8a9aa49..e9c9ac2 100644
--- a/src/args.c
+++ b/src/args.c
@@ -46,7 +46,16 @@
 
 /* Long options */
 enum {
-	OPT_FMT_APT = 1000,
+	OPT_FILTER_TYPE = 1000,
+	OPT_FILTER_START_FROM,
+	OPT_FILTER_START_TO,
+	OPT_FILTER_START_AFTER,
+	OPT_FILTER_START_BEFORE,
+	OPT_FILTER_END_FROM,
+	OPT_FILTER_END_TO,
+	OPT_FILTER_END_AFTER,
+	OPT_FILTER_END_BEFORE,
+	OPT_FMT_APT,
 	OPT_FMT_RAPT,
 	OPT_FMT_EV,
 	OPT_FMT_REV,
@@ -448,6 +457,52 @@ date_arg_extended(const char *startday, const char *range, int add_line,
 	}
 }
 
+static int parse_datearg(const char *str)
+{
+	struct date day;
+
+	if (parse_date(str, DATEFMT_YYYYMMDD, (int *)&day.yyyy,
+			(int *)&day.mm, (int *)&day.dd, NULL))
+		return date2sec(day, 0, 0);
+
+	if (parse_date(str, DATEFMT_MMDDYYYY, (int *)&day.yyyy,
+			(int *)&day.mm, (int *)&day.dd, NULL))
+		return date2sec(day, 0, 0);
+
+	if (parse_date(str, DATEFMT_ISO, (int *)&day.yyyy,
+			(int *)&day.mm, (int *)&day.dd, NULL))
+		return date2sec(day, 0, 0);
+
+	return -1;
+}
+
+static int parse_type_mask(const char *str)
+{
+	char *buf = mem_strdup(str), *p;
+	int mask = 0;
+
+	for (p = strtok(buf, ","); p; p = strtok(NULL, ",")) {
+		if (!strcmp(p, "event")) {
+			mask |= TYPE_MASK_EVNT;
+		} else if (!strcmp(p, "apt")) {
+			mask |= TYPE_MASK_APPT;
+		} else if (!strcmp(p, "recur-event")) {
+			mask |= TYPE_MASK_RECUR_EVNT;
+		} else if (!strcmp(p, "recur-apt")) {
+			mask |= TYPE_MASK_RECUR_APPT;
+		} else if (!strcmp(p, "recur")) {
+			mask |= TYPE_MASK_RECUR;
+		} else {
+			mask = 0;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	mem_free(buf);
+	return mask;
+}
+
 /*
  * Parse the command-line arguments and call the appropriate
  * routines to handle those arguments. Also initialize the data paths.
@@ -469,6 +524,8 @@ int parse_args(int argc, char **argv)
 	int tflag = 0;		/* -t: print todo list */
 	int vflag = 0;		/* -v: print version number */
 	int xflag = 0;		/* -x: export data */
+	/* Filters */
+	struct item_filter filter = { TYPE_MASK_ALL, -1, -1, -1, -1 };
 	/* Format strings */
 	const char *fmt_apt = " - %S -> %E\n\t%m\n";
 	const char *fmt_rapt = " - %S -> %E\n\t%m\n";
@@ -511,6 +568,15 @@ int parse_args(int argc, char **argv)
 		{"version", no_argument, NULL, 'v'},
 		{"export", optional_argument, NULL, 'x'},
 
+		{"filter-type", required_argument, NULL, OPT_FILTER_TYPE},
+		{"filter-start-from", required_argument, NULL, OPT_FILTER_START_FROM},
+		{"filter-start-to", required_argument, NULL, OPT_FILTER_START_TO},
+		{"filter-start-after", required_argument, NULL, OPT_FILTER_START_AFTER},
+		{"filter-start-before", required_argument, NULL, OPT_FILTER_START_BEFORE},
+		{"filter-end-from", required_argument, NULL, OPT_FILTER_END_FROM},
+		{"filter-end-to", required_argument, NULL, OPT_FILTER_END_TO},
+		{"filter-end-after", required_argument, NULL, OPT_FILTER_END_AFTER},
+		{"filter-end-before", required_argument, NULL, OPT_FILTER_END_BEFORE},
 		{"format-apt", required_argument, NULL, OPT_FMT_APT},
 		{"format-recur-apt", required_argument, NULL, OPT_FMT_RAPT},
 		{"format-event", required_argument, NULL, OPT_FMT_EV},
@@ -625,6 +691,51 @@ int parse_args(int argc, char **argv)
 				xfmt = IO_EXPORT_ICAL;
 			}
 			break;
+		case OPT_FILTER_TYPE:
+			filter.type_mask = parse_type_mask(optarg);
+			EXIT_IF(filter.type_mask == 0,
+				_("invalid filter mask"));
+			break;
+		case OPT_FILTER_START_FROM:
+			filter.start_from = parse_datearg(optarg);
+			EXIT_IF(filter.start_from == -1,
+				_("invalid filter start date"));
+			break;
+		case OPT_FILTER_START_TO:
+			filter.start_to = parse_datearg(optarg);
+			EXIT_IF(filter.start_to == -1,
+				_("invalid filter end date"));
+			break;
+		case OPT_FILTER_START_AFTER:
+			filter.start_from = parse_datearg(optarg) + 1;
+			EXIT_IF(filter.start_from == -1,
+				_("invalid filter start date"));
+			break;
+		case OPT_FILTER_START_BEFORE:
+			filter.start_to = parse_datearg(optarg) - 1;
+			EXIT_IF(filter.start_to == -1,
+				_("invalid filter end date"));
+			break;
+		case OPT_FILTER_END_FROM:
+			filter.end_from = parse_datearg(optarg);
+			EXIT_IF(filter.end_from == -1,
+				_("invalid filter start date"));
+			break;
+		case OPT_FILTER_END_TO:
+			filter.end_to = parse_datearg(optarg);
+			EXIT_IF(filter.end_to == -1,
+				_("invalid filter end date"));
+			break;
+		case OPT_FILTER_END_AFTER:
+			filter.end_from = parse_datearg(optarg) + 1;
+			EXIT_IF(filter.end_from == -1,
+				_("invalid filter start date"));
+			break;
+		case OPT_FILTER_END_BEFORE:
+			filter.end_to = parse_datearg(optarg) - 1;
+			EXIT_IF(filter.end_to == -1,
+				_("invalid filter end date"));
+			break;
 		case OPT_FMT_APT:
 			fmt_apt = optarg;
 			break;
@@ -689,7 +800,7 @@ int parse_args(int argc, char **argv)
 			io_check_dir(path_notes);
 			io_check_file(path_apts);
 			io_check_file(path_todo);
-			io_load_app();
+			io_load_app(&filter);
 			io_load_todo();
 			note_gc();
 			non_interactive = 1;
@@ -704,7 +815,7 @@ int parse_args(int argc, char **argv)
 				io_check_file(path_todo);
 				/* Get default pager in case we need to show a log file. */
 				vars_init();
-				io_load_app();
+				io_load_app(&filter);
 				io_load_todo();
 				io_import_data(IO_IMPORT_ICAL, ifile);
 				io_save_apts(path_apts);
@@ -714,7 +825,7 @@ int parse_args(int argc, char **argv)
 			if (xflag) {
 				io_check_file(path_apts);
 				io_check_file(path_todo);
-				io_load_app();
+				io_load_app(&filter);
 				io_load_todo();
 				io_export_data(xfmt);
 				non_interactive = 1;
@@ -728,14 +839,14 @@ int parse_args(int argc, char **argv)
 			}
 			if (nflag) {
 				io_check_file(path_apts);
-				io_load_app();
+				io_load_app(&filter);
 				next_arg();
 				non_interactive = 1;
 			}
 			if (dflag || rflag || sflag) {
 				io_check_file(path_apts);
 				io_check_file(path_conf);
-				io_load_app();
+				io_load_app(&filter);
 				config_load();	/* To get output date format. */
 				if (dflag)
 					date_arg(ddate, add_line, fmt_apt,
@@ -756,7 +867,7 @@ int parse_args(int argc, char **argv)
 				io_check_file(path_conf);
 				vars_init();
 				config_load();	/* To get output date format. */
-				io_load_app();
+				io_load_app(&filter);
 				day.dd = day.mm = day.yyyy = 0;
 				app_arg(add_line, &day, 0, fmt_apt, fmt_rapt,
 						fmt_ev, fmt_rev, preg, &limit);
diff --git a/src/calcurse.c b/src/calcurse.c
index ffd7020..242a142 100644
--- a/src/calcurse.c
+++ b/src/calcurse.c
@@ -333,7 +333,7 @@ static inline void key_generic_reload(void)
 	todo_init_list();
 
 	io_load_todo();
-	io_load_app();
+	io_load_app(NULL);
 	io_unset_modified();
 	ui_todo_load_items();
 	ui_todo_sel_reset();
@@ -684,7 +684,7 @@ int main(int argc, char **argv)
 	wins_erase_status_bar();
 	io_load_keys(conf.pager);
 	io_load_todo();
-	io_load_app();
+	io_load_app(NULL);
 	io_unset_modified();
 	wins_slctd_set(conf.default_panel);
 	wins_resize();
diff --git a/src/calcurse.h b/src/calcurse.h
index 0fc3f62..4ae6c66 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -383,6 +383,23 @@ enum day_item_type {
 	APPT
 };
 
+/* Available item type masks. */
+#define TYPE_MASK_EVNT (1 << EVNT)
+#define TYPE_MASK_APPT (1 << APPT)
+#define TYPE_MASK_RECUR_EVNT (1 << RECUR_EVNT)
+#define TYPE_MASK_RECUR_APPT (1 << RECUR_APPT)
+#define TYPE_MASK_RECUR (TYPE_MASK_RECUR_EVNT | TYPE_MASK_RECUR_APPT)
+#define TYPE_MASK_ALL (TYPE_MASK_EVNT | TYPE_MASK_APPT | TYPE_MASK_RECUR)
+
+/* Filter settings. */
+struct item_filter {
+	int type_mask;
+	long start_from;
+	long start_to;
+	long end_from;
+	long end_to;
+};
+
 /* Generic item description (to hold appointments, events...). */
 struct day_item {
 	enum day_item_type type;
@@ -643,7 +660,8 @@ struct apoint *apoint_new(char *, char *, long, long, char);
 unsigned apoint_inday(struct apoint *, long *);
 void apoint_sec2str(struct apoint *, long, char *, char *);
 void apoint_write(struct apoint *, FILE *);
-struct apoint *apoint_scan(FILE *, struct tm, struct tm, char, char *);
+struct apoint *apoint_scan(FILE *, struct tm, struct tm, char, char *,
+			   struct item_filter *);
 void apoint_delete(struct apoint *);
 struct notify_app *apoint_check_next(struct notify_app *, long);
 void apoint_switch_notify(struct apoint *);
@@ -735,7 +753,7 @@ void event_llist_free(void);
 struct event *event_new(char *, char *, long, int);
 unsigned event_inday(struct event *, long *);
 void event_write(struct event *, FILE *);
-struct event *event_scan(FILE *, struct tm, int, char *);
+struct event *event_scan(FILE *, struct tm, int, char *, struct item_filter *);
 void event_delete(struct event *);
 void event_paste_item(struct event *, long);
 
@@ -761,7 +779,7 @@ unsigned io_save_apts(const char *);
 unsigned io_save_todo(const char *);
 unsigned io_save_keys(void);
 void io_save_cal(enum save_display);
-void io_load_app(void);
+void io_load_app(struct item_filter *);
 void io_load_todo(void);
 void io_load_keys(const char *);
 int io_check_dir(const char *);
@@ -911,9 +929,10 @@ char recur_def2char(enum recur_type);
 int recur_char2def(char);
 struct recur_apoint *recur_apoint_scan(FILE *, struct tm, struct tm,
 				       char, int, struct tm, char *,
-				       llist_t *, char);
+				       llist_t *, char, struct item_filter *);
 struct recur_event *recur_event_scan(FILE *, struct tm, int, char,
-				     int, struct tm, char *, llist_t *);
+				     int, struct tm, char *, llist_t *,
+				     struct item_filter *);
 void recur_apoint_write(struct recur_apoint *, FILE *);
 void recur_event_write(struct recur_event *, FILE *);
 void recur_save_data(FILE *);
diff --git a/src/dmon.c b/src/dmon.c
index c51ee70..546847f 100644
--- a/src/dmon.c
+++ b/src/dmon.c
@@ -165,7 +165,7 @@ void dmon_start(int parent_exit_status)
 	event_llist_init();
 	recur_event_llist_init();
 	todo_init_list();
-	io_load_app();
+	io_load_app(NULL);
 	data_loaded = 1;
 
 	DMON_LOG(_("started at %s\n"), nowstr());
diff --git a/src/event.c b/src/event.c
index 38305b8..96ac41d 100644
--- a/src/event.c
+++ b/src/event.c
@@ -120,7 +120,8 @@ void event_write(struct event *o, FILE * f)
 }
 
 /* Load the events from file */
-struct event *event_scan(FILE * f, struct tm start, int id, char *note)
+struct event *event_scan(FILE * f, struct tm start, int id, char *note,
+			 struct item_filter *filter)
 {
 	char buf[BUFSIZ], *nl;
 	time_t tstart;
@@ -147,6 +148,16 @@ struct event *event_scan(FILE * f, struct tm start, int id, char *note)
 	tstart = mktime(&start);
 	EXIT_IF(tstart == -1, _("date error in the event\n"));
 
+	/* Filter item. */
+	if (filter) {
+		if (!(filter->type_mask & TYPE_MASK_EVNT))
+			return NULL;
+		if (filter->start_from >= 0 && tstart < filter->start_from)
+			return NULL;
+		if (filter->start_to >= 0 && tstart > filter->start_to)
+			return NULL;
+	}
+
 	return event_new(buf, note, tstart, id);
 }
 
diff --git a/src/io.c b/src/io.c
index a50991f..a6c29ac 100644
--- a/src/io.c
+++ b/src/io.c
@@ -464,7 +464,7 @@ static void io_load_error(const char *filename, unsigned line,
  * and then load either: a new appointment, a new event, or a new
  * recursive item (which can also be either an event or an appointment).
  */
-void io_load_app(void)
+void io_load_app(struct item_filter *filter)
 {
 	FILE *data_file;
 	int c, is_appointment, is_event, is_recursive;
@@ -620,18 +620,19 @@ void io_load_app(void)
 			if (is_recursive) {
 				recur_apoint_scan(data_file, start, end,
 						  type, freq, until, notep,
-						  &exc, state);
+						  &exc, state, filter);
 			} else {
 				apoint_scan(data_file, start, end, state,
-					    notep);
+					    notep, filter);
 			}
 		} else if (is_event) {
 			if (is_recursive) {
 				recur_event_scan(data_file, start, id,
 						 type, freq, until, notep,
-						 &exc);
+						 &exc, filter);
 			} else {
-				event_scan(data_file, start, id, notep);
+				event_scan(data_file, start, id, notep,
+					   filter);
 			}
 		} else {
 			io_load_error(path_apts, line,
diff --git a/src/recur.c b/src/recur.c
index 69706b0..76585d6 100644
--- a/src/recur.c
+++ b/src/recur.c
@@ -330,7 +330,8 @@ static void recur_write_exc(llist_t * lexc, FILE * f)
 struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
 				       struct tm end, char type, int freq,
 				       struct tm until, char *note,
-				       llist_t * exc, char state)
+				       llist_t * exc, char state,
+				       struct item_filter *filter)
 {
 	char buf[BUFSIZ], *nl;
 	time_t tstart, tend, tuntil;
@@ -375,6 +376,20 @@ struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
 	EXIT_IF(tstart == -1 || tend == -1 || tstart > tend
 		|| tuntil == -1, _("date error in appointment"));
 
+	/* Filter item. */
+	if (filter) {
+		if (!(filter->type_mask & TYPE_MASK_RECUR_APPT))
+			return NULL;
+		if (filter->start_from >= 0 && tstart < filter->start_from)
+			return NULL;
+		if (filter->start_to >= 0 && tstart > filter->start_to)
+			return NULL;
+		if (filter->end_from >= 0 && tend < filter->end_from)
+			return NULL;
+		if (filter->end_to >= 0 && tend > filter->end_to)
+			return NULL;
+	}
+
 	return recur_apoint_new(buf, note, tstart, tend - tstart, state,
 				recur_char2def(type), freq, tuntil, exc);
 }
@@ -382,7 +397,8 @@ struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
 /* Load the recursive events from file */
 struct recur_event *recur_event_scan(FILE * f, struct tm start, int id,
 				     char type, int freq, struct tm until,
-				     char *note, llist_t * exc)
+				     char *note, llist_t * exc,
+				     struct item_filter *filter)
 {
 	char buf[BUFSIZ], *nl;
 	time_t tstart, tuntil;
@@ -417,6 +433,16 @@ struct recur_event *recur_event_scan(FILE * f, struct tm start, int id,
 	tstart = mktime(&start);
 	EXIT_IF(tstart == -1 || tuntil == -1, _("date error in event"));
 
+	/* Filter item. */
+	if (filter) {
+		if (!(filter->type_mask & TYPE_MASK_RECUR_EVNT))
+			return NULL;
+		if (filter->start_from >= 0 && tstart < filter->start_from)
+			return NULL;
+		if (filter->start_to >= 0 && tstart > filter->start_to)
+			return NULL;
+	}
+
 	return recur_event_new(buf, note, tstart, id, recur_char2def(type),
 			       freq, tuntil, exc);
 }
-- 
cgit v1.2.3-70-g09d2