From 7f8c62bf57b85234c248316505a503602792839a Mon Sep 17 00:00:00 2001
From: Lukas Fleischer <lfleischer@calcurse.org>
Date: Mon, 11 Jan 2016 22:26:46 +0100
Subject: Add an option to filter by object hash

Implement a new --filter-hash option to filter by object identifiers.
Each object having an identifier that has the specified pattern as a
prefix is matched. Patterns starting with an exclamation mark (!) are
interpreted as negative patterns.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
---
 src/apoint.c   | 15 ++++++++++++++-
 src/args.c     |  7 ++++++-
 src/calcurse.h |  2 ++
 src/event.c    | 15 ++++++++++++++-
 src/io.c       | 16 ++++++++++++++--
 src/recur.c    | 32 +++++++++++++++++++++++++++++---
 src/utils.c    | 12 ++++++++++++
 7 files changed, 91 insertions(+), 8 deletions(-)

(limited to 'src')

diff --git a/src/apoint.c b/src/apoint.c
index 23c3163..457317d 100644
--- a/src/apoint.c
+++ b/src/apoint.c
@@ -190,6 +190,7 @@ struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
 {
 	char buf[BUFSIZ], *newline;
 	time_t tstart, tend;
+	struct apoint *apt;
 
 	EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
 		!check_date(end.tm_year, end.tm_mon, end.tm_mday) ||
@@ -233,7 +234,19 @@ struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
 			return NULL;
 	}
 
-	return apoint_new(buf, note, tstart, tend - tstart, state);
+	apt = apoint_new(buf, note, tstart, tend - tstart, state);
+
+	/* Filter by hash. */
+	if (filter && filter->hash) {
+		char *hash = apoint_hash(apt);
+		if (!hash_matches(filter->hash, hash)) {
+			apoint_delete(apt);
+			apt = NULL;
+		}
+		mem_free(hash);
+	}
+
+	return apt;
 }
 
 void apoint_delete(struct apoint *apt)
diff --git a/src/args.c b/src/args.c
index 60e8043..c843d69 100644
--- a/src/args.c
+++ b/src/args.c
@@ -47,6 +47,7 @@
 /* Long options */
 enum {
 	OPT_FILTER_TYPE = 1000,
+	OPT_FILTER_HASH,
 	OPT_FILTER_PATTERN,
 	OPT_FILTER_START_FROM,
 	OPT_FILTER_START_TO,
@@ -442,7 +443,7 @@ int parse_args(int argc, char **argv)
 	int range = -1;
 	int limit = INT_MAX;
 	/* Filters */
-	struct item_filter filter = { 0, NULL, -1, -1, -1, -1, 0, 0, 0 };
+	struct item_filter filter = { 0, NULL, NULL, -1, -1, -1, -1, 0, 0, 0 };
 	/* Format strings */
 	const char *fmt_apt = " - %S -> %E\n\t%m\n";
 	const char *fmt_rapt = " - %S -> %E\n\t%m\n";
@@ -481,6 +482,7 @@ int parse_args(int argc, char **argv)
 		{"query", optional_argument, NULL, 'Q'},
 
 		{"filter-type", required_argument, NULL, OPT_FILTER_TYPE},
+		{"filter-hash", required_argument, NULL, OPT_FILTER_HASH},
 		{"filter-pattern", required_argument, NULL, OPT_FILTER_PATTERN},
 		{"filter-start-from", required_argument, NULL, OPT_FILTER_START_FROM},
 		{"filter-start-to", required_argument, NULL, OPT_FILTER_START_TO},
@@ -604,6 +606,9 @@ int parse_args(int argc, char **argv)
 			EXIT_IF(filter.type_mask == 0,
 				_("invalid filter mask"));
 			break;
+		case OPT_FILTER_HASH:
+			filter.hash = mem_strdup(optarg);
+			break;
 		case 'S':
 		case OPT_FILTER_PATTERN:
 			EXIT_IF(filter.regex,
diff --git a/src/calcurse.h b/src/calcurse.h
index 5724265..41f7681 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -407,6 +407,7 @@ enum item_type {
 /* Filter settings. */
 struct item_filter {
 	int type_mask;
+	char *hash;
 	regex_t *regex;
 	time_t start_from;
 	time_t start_to;
@@ -1120,6 +1121,7 @@ int vasprintf(char **, const char *, va_list);
 int asprintf(char **, const char *, ...);
 int starts_with(const char *, const char *);
 int starts_with_ci(const char *, const char *);
+int hash_matches(const char *, const char *);
 
 /* vars.c */
 extern int col, row;
diff --git a/src/event.c b/src/event.c
index 1e9d940..e0891a2 100644
--- a/src/event.c
+++ b/src/event.c
@@ -147,6 +147,7 @@ struct event *event_scan(FILE * f, struct tm start, int id, char *note,
 {
 	char buf[BUFSIZ], *nl;
 	time_t tstart, tend;
+	struct event *ev;
 
 	EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
 		!check_time(start.tm_hour, start.tm_min),
@@ -187,7 +188,19 @@ struct event *event_scan(FILE * f, struct tm start, int id, char *note,
 			return NULL;
 	}
 
-	return event_new(buf, note, tstart, id);
+	ev = event_new(buf, note, tstart, id);
+
+	/* Filter by hash. */
+	if (filter && filter->hash) {
+		char *hash = event_hash(ev);
+		if (!hash_matches(filter->hash, hash)) {
+			event_delete(ev);
+			ev = NULL;
+		}
+		mem_free(hash);
+	}
+
+	return ev;
 }
 
 /* Delete an event from the list. */
diff --git a/src/io.c b/src/io.c
index fcb13ae..d25180a 100644
--- a/src/io.c
+++ b/src/io.c
@@ -684,8 +684,20 @@ void io_load_todo(struct item_filter *filter)
 				continue;
 		}
 
-		todo_add(e_todo, id, note);
-		++nb_tod;
+		struct todo *todo = todo_add(e_todo, id, note);
+
+		/* Filter by hash. */
+		if (filter && filter->hash) {
+			char *hash = todo_hash(todo);
+			if (!hash_matches(filter->hash, hash)) {
+				todo_delete(todo);
+				todo = NULL;
+			}
+			mem_free(hash);
+		}
+
+		if (todo)
+			++nb_tod;
 	}
 	file_close(data_file, __FILE_POS__);
 }
diff --git a/src/recur.c b/src/recur.c
index ddfddb4..921b5c9 100644
--- a/src/recur.c
+++ b/src/recur.c
@@ -336,6 +336,7 @@ struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
 {
 	char buf[BUFSIZ], *nl;
 	time_t tstart, tend, tuntil;
+	struct recur_apoint *rapt;
 
 	EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
 		!check_date(end.tm_year, end.tm_mon, end.tm_mday) ||
@@ -393,8 +394,20 @@ struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
 			return NULL;
 	}
 
-	return recur_apoint_new(buf, note, tstart, tend - tstart, state,
+	rapt = recur_apoint_new(buf, note, tstart, tend - tstart, state,
 				recur_char2def(type), freq, tuntil, exc);
+
+	/* Filter by hash. */
+	if (filter && filter->hash) {
+		char *hash = recur_apoint_hash(rapt);
+		if (!hash_matches(filter->hash, hash)) {
+			recur_apoint_erase(rapt);
+			rapt = NULL;
+		}
+		mem_free(hash);
+	}
+
+	return rapt;
 }
 
 /* Load the recursive events from file */
@@ -405,6 +418,7 @@ struct recur_event *recur_event_scan(FILE * f, struct tm start, int id,
 {
 	char buf[BUFSIZ], *nl;
 	time_t tstart, tend, tuntil;
+	struct recur_event *rev;
 
 	EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
 		!check_time(start.tm_hour, start.tm_min) ||
@@ -453,8 +467,20 @@ struct recur_event *recur_event_scan(FILE * f, struct tm start, int id,
 			return NULL;
 	}
 
-	return recur_event_new(buf, note, tstart, id, recur_char2def(type),
-			       freq, tuntil, exc);
+	rev = recur_event_new(buf, note, tstart, id, recur_char2def(type),
+			      freq, tuntil, exc);
+
+	/* Filter by hash. */
+	if (filter && filter->hash) {
+		char *hash = recur_event_hash(rev);
+		if (!hash_matches(filter->hash, hash)) {
+			recur_event_erase(rev);
+			rev = NULL;
+		}
+		mem_free(hash);
+	}
+
+	return rev;
 }
 
 char *recur_apoint_tostr(struct recur_apoint *o)
diff --git a/src/utils.c b/src/utils.c
index 58b1d44..528657e 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -1641,3 +1641,15 @@ int starts_with_ci(const char *s, const char *p)
 	for (; *p && tolower(*p) == tolower(*s); s++, p++);
 	return (*p == '\0');
 }
+
+int hash_matches(const char *pattern, const char *hash)
+{
+	int invert = 0;
+
+	if (pattern[0] == '!') {
+		invert = 1;
+		pattern++;
+	}
+
+	return (starts_with(hash, pattern) != invert);
+}
-- 
cgit v1.2.3-70-g09d2