[systemd-devel] [PATCH 5/5] calendar: parse months's names
Daniele Medri
dmedri at gmail.com
Thu Oct 30 04:19:15 PDT 2014
---
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
More information about the systemd-devel
mailing list