--- man/systemd.time.xml | 9 +++ src/shared/calendarspec.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++ src/shared/calendarspec.h | 1 + 3 files changed, 212 insertions(+)
diff --git a/man/systemd.time.xml b/man/systemd.time.xml index 2ee5858..d108871 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -225,6 +225,15 @@ range of continuous weekdays. <literal>,</literal> and <literal>-</literal> may be combined freely.</para> + <para>The months's names specification is available too. + It should consist of one or more English + language months's names, either in the abbreviated + (Apr) or non-abbreviated (April) form (case does + not matter), separated by commas. Specifying two + months separated by <literal>-</literal> refers to a + range of continuous months. <literal>,</literal> and + <literal>-</literal> may be combined freely.</para> + <para>In the date and time specifications, any component may be specified as <literal>*</literal> in which case any value will match. Alternatively, each diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index 9b05f13..dea83ce 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -25,6 +25,7 @@ #include "calendarspec.h" #define BITS_WEEKDAYS 127 +#define BITS_MONTHS 4095 /* 0^2 + 1^2 + ... + 11^2 */ static void free_chain(CalendarComponent *c) { CalendarComponent *n; @@ -125,6 +126,9 @@ int calendar_spec_normalize(CalendarSpec *c) { if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS) c->weekdays_bits = -1; + if (c->months_bits <= 0 || c->months_bits >= BITS_MONTHS) + c->months_bits = -1; + fix_year(c->year); sort_chain(&c->year); @@ -159,6 +163,9 @@ _pure_ bool calendar_spec_valid(CalendarSpec *c) { if (c->weekdays_bits > BITS_WEEKDAYS) return false; + if (c->months_bits > BITS_MONTHS) + return false; + if (!chain_valid(c->year, 1970, 2199)) return false; @@ -229,6 +236,60 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { } } +static void format_months(FILE *f, const CalendarSpec *c) { + static const char *const months[] = { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + + int l, x; + bool need_colon = false; + + assert(f); + assert(c); + assert(c->months_bits > 0 && c->months_bits <= BITS_MONTHS); + + for (x = 0, l = -1; x < (int) ELEMENTSOF(months); x++) { + + if (c->months_bits & (1 << x)) { + + if (l < 0) { + if (need_colon) + fputc(',', f); + else + need_colon = true; + + fputs(months[x], f); + l = x; + } + + } else if (l >= 0) { + + if (x > l + 1) { + fputc(x > l + 2 ? '-' : ',', f); + fputs(months[x-1], f); + } + + l = -1; + } + } + + if (l >= 0 && x > l + 1) { + fputc(x > l + 2 ? '-' : ',', f); + fputs(months[x-1], f); + } +} + static void format_chain(FILE *f, int space, const CalendarComponent *c) { assert(f); @@ -266,6 +327,11 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { fputc(' ', f); } + if (c->months_bits > 0 && c->months_bits <= BITS_MONTHS) { + format_months(f, c); + fputc(' ', f); + } + format_chain(f, 4, c->year); fputc('-', f); format_chain(f, 2, c->month); @@ -385,6 +451,108 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { } } +static int parse_months(const char **p, CalendarSpec *c) { + static const struct { + const char *name; + const int nr; + } month_nr[] = { + { "January", 0 }, + { "Jan", 0 }, + { "February", 1 }, + { "Feb", 1 }, + { "March", 2 }, + { "Mar", 2 }, + { "April", 3 }, + { "Apr", 3 }, + { "May", 4 }, + { "June", 5 }, + { "Jun", 5 }, + { "July", 6 }, + { "Jul", 6 }, + { "August", 7 }, + { "Aug", 7 }, + { "September", 8 }, + { "Sep", 8 }, + { "October", 9 }, + { "Oct", 9 }, + { "November", 10 }, + { "Nov", 10 }, + { "December", 11 }, + { "Dec", 11 } + }; + + int l = -1; + bool first = true; + + assert(p); + assert(*p); + assert(c); + + for (;;) { + unsigned i; + + if (!first && **p == ' ') + return 0; + + for (i = 0; i < ELEMENTSOF(month_nr); i++) { + size_t skip; + + if (!startswith_no_case(*p, month_nr[i].name)) + continue; + + skip = strlen(month_nr[i].name); + + if ((*p)[skip] != '-' && + (*p)[skip] != ',' && + (*p)[skip] != ' ' && + (*p)[skip] != 0) + return -EINVAL; + + c->months_bits |= 1 << month_nr[i].nr; + + if (l >= 0) { + int j; + + if (l > month_nr[i].nr) + return -EINVAL; + + for (j = l + 1; j < month_nr[i].nr; j++) + c->months_bits |= 1 << j; + } + + *p += skip; + break; + } + + /* Couldn't find this prefix, so let's assume the + weekday was not specified and let's continue with + the date */ + if (i >= ELEMENTSOF(month_nr)) + return first ? 0 : -EINVAL; + + /* We reached the end of the string */ + if (**p == 0) + return 0; + + /* We reached the end of the month spec part */ + if (**p == ' ') { + *p += strspn(*p, " "); + return 0; + } + + if (**p == '-') { + if (l >= 0) + return -EINVAL; + + l = month_nr[i].nr; + } else + l = -1; + + *p += 1; + first = false; + } +} + static int prepend_component(const char **p, CalendarComponent **c) { unsigned long value, repeat = 0; char *e = NULL, *ee = NULL; @@ -573,6 +741,9 @@ static int parse_time(const char **p, CalendarSpec *c) { if (c->day || c->weekdays_bits > 0) goto null_hour; + if (c->month || c->months_bits > 0) + goto null_hour; + goto finish; } @@ -786,6 +957,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (r < 0) goto fail; + r = parse_months(&p, c); + if (r < 0) + goto fail; + r = parse_date(&p, c); if (r < 0) goto fail; @@ -894,6 +1069,21 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm) { return (weekdays_bits & (1 << k)); } +static bool matches_month(int months_bits, const struct tm *tm) { + struct tm t; + int k; + + if (months_bits < 0 || months_bits >= BITS_MONTHS) + return true; + + t = *tm; + if (mktime(&t) == (time_t) -1) + return false; + + k = t.tm_mon == 0 ? 11 : t.tm_mon - 1; + return (months_bits & (1 << k)); +} + static int find_next(const CalendarSpec *spec, struct tm *tm) { struct tm c; int r; @@ -908,6 +1098,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { mktime(&c); c.tm_isdst = -1; + /* years */ c.tm_year += 1900; r = find_matching_component(spec->year, &c.tm_year); c.tm_year -= 1900; @@ -920,6 +1111,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { if (r < 0 || tm_out_of_bounds(&c)) return r; + /* months */ c.tm_mon += 1; r = find_matching_component(spec->month, &c.tm_mon); c.tm_mon -= 1; @@ -936,6 +1128,13 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { continue; } + if (!matches_month(spec->months_bits, &c)) { + c.tm_mday = 1; + c.tm_hour = c.tm_min = c.tm_sec = 0; + continue; + } + + /* weekdays */ r = find_matching_component(spec->day, &c.tm_mday); if (r > 0) c.tm_hour = c.tm_min = c.tm_sec = 0; @@ -952,6 +1151,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { continue; } + /* hours */ r = find_matching_component(spec->hour, &c.tm_hour); if (r > 0) c.tm_min = c.tm_sec = 0; @@ -961,6 +1161,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { continue; } + /* minutes */ r = find_matching_component(spec->minute, &c.tm_min); if (r > 0) c.tm_sec = 0; @@ -970,6 +1171,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { continue; } + /* seconds */ r = find_matching_component(spec->second, &c.tm_sec); if (r < 0 || tm_out_of_bounds(&c)) { c.tm_min ++; diff --git a/src/shared/calendarspec.h b/src/shared/calendarspec.h index 7baf318..2431fb7 100644 --- a/src/shared/calendarspec.h +++ b/src/shared/calendarspec.h @@ -36,6 +36,7 @@ typedef struct CalendarComponent { typedef struct CalendarSpec { int weekdays_bits; + int months_bits; CalendarComponent *year; CalendarComponent *month; -- 1.9.3 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel