[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