about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2015-12-27 10:32:10 +0100
committerChristian Neukirchen <chneukirchen@gmail.com>2015-12-27 10:32:10 +0100
commit481cfb471acea91a59b58d2ec5384009e422dcbd (patch)
tree0de5c9168c635fe906f95b99b0d5e8314b232eac
parent1ca8ef43eb1f9d5e5904f92045c52b1b91a88f26 (diff)
downloadlr-481cfb471acea91a59b58d2ec5384009e422dcbd.tar.gz
lr-481cfb471acea91a59b58d2ec5384009e422dcbd.tar.xz
lr-481cfb471acea91a59b58d2ec5384009e422dcbd.zip
add relative and parsed date comparisons
-rw-r--r--README.md19
-rw-r--r--lr.c120
2 files changed, 128 insertions, 11 deletions
diff --git a/README.md b/README.md
index fd6d051..afa9c17 100644
--- a/README.md
+++ b/README.md
@@ -121,17 +121,32 @@ Default: `n`.
 	             | <expr> && <expr>  -- conjunction
 	             | ! <expr>          -- negation
 	             | ( <expr )
+	             | <timeprop> <numop> <dur>
 	             | <numprop> <numop> <num>
 	             | <strprop> <strop> <str>
 	             | <typetest>
 	             | <modetest>
 	             | prune             -- do not traverse into subdirectories
 	             | print             -- always true value
+
+        <timeprop> ::= atime | ctime | mtime
 	
-	<numprop>  ::= atime | ctime | depth | dev | entries | gid | inode
-	             | links | mode | mtime | rdev | size | total | uid
+	<numprop>  ::= depth | dev | entries | gid | inode
+	             | links | mode | rdev | size | total | uid
 	
 	<numop>    ::= <= | < | >= | > | == | !=
+
+        <dur>      ::= "./path"          -- mtime of relative path
+                     | "/path"           -- mtime of absolute path
+                     | "YYYY-MM-DD HH:MM:SS"
+                     | "YYYY-MM-DD"      -- at midnight
+                     | "HH:MM:SS"        -- today
+                     | "HH:MM"           -- today
+                     | "-[0-9]+d"        -- n days ago at midnight
+                     | "-[0-9]+h"        -- n hours before now
+                     | "-[0-9]+m"        -- n minutes before now
+                     | "-[0-9]+s"        -- n seconds before now
+                     | [0-9]+            -- absolute epoch time
 	
 	<num>      ::= [0-9]+ ( c        -- *1
 	                      | b        -- *512
diff --git a/lr.c b/lr.c
index 29c59e8..b088b4e 100644
--- a/lr.c
+++ b/lr.c
@@ -378,6 +378,84 @@ parse_string(char **s)
 	return 0;
 }
 
+static int
+parse_dur(int64_t *n)
+{
+	char *s, *r;
+	if (!parse_string(&s))
+		return 0;
+
+	if (*s == '/' || *s == '.') {
+		struct stat st;
+		if (stat(s, &st) < 0)
+			parse_error("can't stat file");
+		*n = st.st_mtime;
+		return 1;
+	}
+
+	struct tm tm = { 0 };
+	r = strptime(s, "%Y-%m-%d %H:%M:%S", &tm);
+	if (r && !*r) {
+		*n = mktime(&tm);
+		return 1;
+	}
+	r = strptime(s, "%Y-%m-%d", &tm);
+	if (r && !*r) {
+		tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+		*n = mktime(&tm);
+		return 1;
+	}
+	r = strptime(s, "%H:%M:%S", &tm);
+	if (r && !*r) {
+		struct tm *tmnow = localtime(&now);
+		tm.tm_year = tmnow->tm_year;
+		tm.tm_mon = tmnow->tm_mon;
+		tm.tm_mday = tmnow->tm_mday;
+		*n = mktime(&tm);
+		return 1;
+	}
+	r = strptime(s, "%H:%M", &tm);
+	if (r && !*r) {
+		struct tm *tmnow = localtime(&now);
+		tm.tm_year = tmnow->tm_year;
+		tm.tm_mon = tmnow->tm_mon;
+		tm.tm_mday = tmnow->tm_mday;
+		tm.tm_sec = 0;
+		*n = mktime(&tm);
+		return 1;
+	}
+
+	if (*s == '-') {
+		s++;
+
+		errno = 0;
+		int64_t d;
+		d = strtol(s, &r, 10);
+		if (errno == 0 && r[0] == 'd' && !r[1]) {
+			struct tm *tmnow = localtime(&now);
+			tmnow->tm_mday -= d;
+			tmnow->tm_hour = tmnow->tm_min = tmnow->tm_sec = 0;
+			*n = mktime(tmnow);
+			return 1;
+		}
+		if (errno == 0 && r[0] == 'h' && !r[1]) {
+			*n = now - (d*60*60);
+			return 1;
+		}
+		if (errno == 0 && r[0] == 'm' && !r[1]) {
+			*n = now - (d*60);
+			return 1;
+		}
+		if (errno == 0 && r[0] == 's' && !r[1]) {
+			*n = now - d;
+			return 1;
+		}
+	}
+
+	parse_error("invalid time format");
+	return 0;
+}
+
 static struct expr *
 parse_strcmp()
 {
@@ -477,11 +555,7 @@ parse_cmp()
 	enum prop prop;
 	enum op op;
 
-	if (token("atime"))
-		prop = PROP_ATIME;
-	else if (token("ctime"))
-		prop = PROP_CTIME;
-	else if (token("depth"))
+	if (token("depth"))
 		prop = PROP_DEPTH;
 	else if (token("dev"))
 		prop = PROP_DEV;
@@ -495,8 +569,6 @@ parse_cmp()
 		prop = PROP_LINKS;
 	else if (token("mode"))
 		return parse_mode();
-	else if (token("mtime"))
-		prop = PROP_MTIME;
 	else if (token("rdev"))
 		prop = PROP_RDEV;
 	else if (token("size"))
@@ -524,6 +596,36 @@ parse_cmp()
 }
 
 static struct expr *
+parse_timecmp()
+{
+	enum prop prop;
+	enum op op;
+
+	if (token("atime"))
+		prop = PROP_ATIME;
+	else if (token("ctime"))
+		prop = PROP_CTIME;
+	else if (token("mtime"))
+		prop = PROP_MTIME;
+	else
+		return parse_cmp();
+
+	op = parse_op();
+	if (!op)
+		parse_error("invalid comparison");
+
+	int64_t n;
+	if (parse_num(&n) || parse_dur(&n)) {
+		struct expr *e = mkexpr(op);
+		e->a.prop = prop;
+		e->b.num = n;
+		return e;
+	}
+
+	return 0;
+}
+
+static struct expr *
 chain(struct expr *e1, enum op op, struct expr *e2)
 {
 	struct expr *i, *j, *e;
@@ -548,11 +650,11 @@ chain(struct expr *e1, enum op op, struct expr *e2)
 static struct expr *
 parse_and()
 {
-	struct expr *e1 = parse_cmp();
+	struct expr *e1 = parse_timecmp();
 	struct expr *r = e1;
 
 	while (token("&&")) {
-		struct expr *e2 = parse_cmp();
+		struct expr *e2 = parse_timecmp();
 		r = chain(r, EXPR_AND, e2);
 	}