[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