[systemd-devel] [PATCH 5/5] calendar: parse months's names

Lennart Poettering lennart at poettering.net
Mon Nov 10 16:05:24 PST 2014


On Thu, 30.10.14 12:19, Daniele Medri (dmedri at gmail.com) wrote:

As mentiond in my early review, such a bitfield would duplicate the
"months" chain we already have in place.

I am totally onboard with allowing a syntax where month names are
used, but this really should be translated into the normal chain stuff
internally, to avoid a redundant structure in memory!

Lennart

> ---
>  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 at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/systemd-devel


Lennart

-- 
Lennart Poettering, Red Hat


More information about the systemd-devel mailing list