From 32b5236916296044a89532025e9fb5ef7e68ca8a Mon Sep 17 00:00:00 2001 From: Douglas Christman Date: Thu, 30 Jun 2016 20:16:05 -0400 Subject: calendarspec: allow ranges in date and time specifications Resolves #3042 --- src/basic/calendarspec.c | 64 +++++++++++++++++++++++++++++++------------- src/test/test-calendarspec.c | 5 ++++ 2 files changed, 50 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 6e0bab9b94..54ab909ddf 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -32,6 +32,8 @@ #include "parse-util.h" #include "string-util.h" +/* Longest valid date/time range is 1970..2199 */ +#define MAX_RANGE_LEN 230 #define BITS_WEEKDAYS 127 static void free_chain(CalendarComponent *c) { @@ -448,8 +450,26 @@ static int parse_component_decimal(const char **p, bool usec, unsigned long *res return 0; } +static int const_chain(int value, CalendarComponent **c) { + CalendarComponent *cc = NULL; + + assert(c); + + cc = new0(CalendarComponent, 1); + if (!cc) + return -ENOMEM; + + cc->value = value; + cc->repeat = 0; + cc->next = *c; + + *c = cc; + + return 0; +} + static int prepend_component(const char **p, bool usec, CalendarComponent **c) { - unsigned long value, repeat = 0; + unsigned long i, value, range_end, range_inc, repeat = 0; CalendarComponent *cc; int r; const char *e; @@ -471,6 +491,30 @@ static int prepend_component(const char **p, bool usec, CalendarComponent **c) { if (repeat == 0) return -ERANGE; + } else if (e[0] == '.' && e[1] == '.') { + e += 2; + r = parse_component_decimal(&e, usec, &range_end); + if (r < 0) + return r; + + if (value >= range_end) + return -EINVAL; + + range_inc = usec ? USEC_PER_SEC : 1; + + /* Don't allow impossibly large ranges... */ + if (range_end - value >= MAX_RANGE_LEN * range_inc) + return -EINVAL; + + /* ...or ranges with only a single element */ + if (range_end - value < range_inc) + return -EINVAL; + + for (i = value; i <= range_end; i += range_inc) { + r = const_chain(i, c); + if (r < 0) + return r; + } } if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':') @@ -495,24 +539,6 @@ static int prepend_component(const char **p, bool usec, CalendarComponent **c) { return 0; } -static int const_chain(int value, CalendarComponent **c) { - CalendarComponent *cc = NULL; - - assert(c); - - cc = new0(CalendarComponent, 1); - if (!cc) - return -ENOMEM; - - cc->value = value; - cc->repeat = 0; - cc->next = *c; - - *c = cc; - - return 0; -} - static int parse_chain(const char **p, bool usec, CalendarComponent **c) { const char *t; CalendarComponent *cc = NULL; diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 5a8c6cbfb6..cb2538ed7f 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -124,6 +124,10 @@ int main(int argc, char* argv[]) { test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001"); test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000"); test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000"); + test_one("9..11,13:00,30", "*-*-* 09,10,11,13:00,30:00"); + test_one("1..3-1..3 1..3:1..3", "*-01,02,03-01,02,03 01,02,03:01,02,03:00"); + test_one("00:00:1.125..2.125", "*-*-* 00:00:01.125000,02.125000"); + test_one("00:00:1.0..3.8", "*-*-* 00:00:01,02,03"); test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000); test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000); @@ -146,6 +150,7 @@ int main(int argc, char* argv[]) { assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0); assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0); assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0); + assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) < 0); return 0; } -- cgit v1.2.3-54-g00ecf From e638d0504fa4861896f47c19e22b48c7f8ca3478 Mon Sep 17 00:00:00 2001 From: Douglas Christman Date: Thu, 30 Jun 2016 22:26:07 -0400 Subject: calendarspec: use ".." notation for ranges of weekdays For backwards compatibility, both the new format (Mon..Wed) and the old format (Mon-Wed) are supported. --- man/systemd.time.xml | 10 +++++----- src/basic/calendarspec.c | 24 ++++++++++++++++++------ src/test/test-calendarspec.c | 7 +++++-- 3 files changed, 28 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/man/systemd.time.xml b/man/systemd.time.xml index 6f9e88406d..9f0b2e1120 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -217,8 +217,8 @@ should consist of one or more English language weekday names, either in the abbreviated (Wed) or non-abbreviated (Wednesday) form (case does not matter), separated by commas. Specifying two - weekdays separated by - refers to a range of - continuous weekdays. , and - + weekdays separated by .. refers to a range of + continuous weekdays. , and .. may be combined freely. In the date and time specifications, any component may be @@ -263,12 +263,12 @@ Examples for valid timestamps and their normalized form: - Sat,Thu,Mon-Wed,Sat-Sun → Mon-Thu,Sat,Sun *-*-* 00:00:00 + Sat,Thu,Mon..Wed,Sat..Sun → Mon..Thu,Sat,Sun *-*-* 00:00:00 Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00 Wed *-1 → Wed *-*-01 00:00:00 - Wed-Wed,Wed *-1 → Wed *-*-01 00:00:00 + Wed..Wed,Wed *-1 → Wed *-*-01 00:00:00 Wed, 17:48 → Wed *-*-* 17:48:00 -Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03 +Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03 *-*-7 0:0:0 → *-*-07 00:00:00 10-15 → *-10-15 00:00:00 monday *-12-* 17:00 → Mon *-12-* 17:00:00 diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 54ab909ddf..e4cfab364e 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -202,7 +202,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { }; int l, x; - bool need_colon = false; + bool need_comma = false; assert(f); assert(c); @@ -213,10 +213,10 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { if (c->weekdays_bits & (1 << x)) { if (l < 0) { - if (need_colon) + if (need_comma) fputc(',', f); else - need_colon = true; + need_comma = true; fputs(days[x], f); l = x; @@ -225,7 +225,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { } else if (l >= 0) { if (x > l + 1) { - fputc(x > l + 2 ? '-' : ',', f); + fputs(x > l + 2 ? ".." : ",", f); fputs(days[x-1], f); } @@ -234,7 +234,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { } if (l >= 0 && x > l + 1) { - fputc(x > l + 2 ? '-' : ',', f); + fputs(x > l + 2 ? ".." : ",", f); fputs(days[x-1], f); } } @@ -359,6 +359,7 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { skip = strlen(day_nr[i].name); if ((*p)[skip] != '-' && + (*p)[skip] != '.' && (*p)[skip] != ',' && (*p)[skip] != ' ' && (*p)[skip] != 0) @@ -396,7 +397,18 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { return 0; } - if (**p == '-') { + if (**p == '.') { + if (l >= 0) + return -EINVAL; + + if ((*p)[1] != '.') + return -EINVAL; + + l = day_nr[i].nr; + *p += 1; + + /* Support ranges with "-" for backwards compatibility */ + } else if (**p == '-') { if (l >= 0) return -EINVAL; diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index cb2538ed7f..4a2b93de59 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -91,12 +91,15 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_ int main(int argc, char* argv[]) { CalendarSpec *c; - test_one("Sat,Thu,Mon-Wed,Sat-Sun", "Mon-Thu,Sat,Sun *-*-* 00:00:00"); + test_one("Sat,Thu,Mon-Wed,Sat-Sun", "Mon..Thu,Sat,Sun *-*-* 00:00:00"); + test_one("Sat,Thu,Mon..Wed,Sat..Sun", "Mon..Thu,Sat,Sun *-*-* 00:00:00"); test_one("Mon,Sun 12-*-* 2,1:23", "Mon,Sun 2012-*-* 01,02:23:00"); test_one("Wed *-1", "Wed *-*-01 00:00:00"); test_one("Wed-Wed,Wed *-1", "Wed *-*-01 00:00:00"); + test_one("Wed..Wed,Wed *-1", "Wed *-*-01 00:00:00"); test_one("Wed, 17:48", "Wed *-*-* 17:48:00"); - test_one("Wed-Sat,Tue 12-10-15 1:2:3", "Tue-Sat 2012-10-15 01:02:03"); + test_one("Wed-Sat,Tue 12-10-15 1:2:3", "Tue..Sat 2012-10-15 01:02:03"); + test_one("Wed..Sat,Tue 12-10-15 1:2:3", "Tue..Sat 2012-10-15 01:02:03"); test_one("*-*-7 0:0:0", "*-*-07 00:00:00"); test_one("10-15", "*-10-15 00:00:00"); test_one("monday *-12-* 17:00", "Mon *-12-* 17:00:00"); -- cgit v1.2.3-54-g00ecf