#include "alloc-util.h"
#include "calendarspec.h"
#include "fileio.h"
+#include "parse-util.h"
#include "string-util.h"
#define BITS_WEEKDAYS 127
free_chain(c->day);
free_chain(c->hour);
free_chain(c->minute);
- free_chain(c->second);
+ free_chain(c->microsecond);
free(c);
}
sort_chain(&c->day);
sort_chain(&c->hour);
sort_chain(&c->minute);
- sort_chain(&c->second);
+ sort_chain(&c->microsecond);
return 0;
}
if (!chain_valid(c->minute, 0, 59))
return false;
- if (!chain_valid(c->second, 0, 59))
+ if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1))
return false;
return true;
}
}
-static void format_chain(FILE *f, int space, const CalendarComponent *c) {
+static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
assert(f);
if (!c) {
}
assert(c->value >= 0);
- fprintf(f, "%0*i", space, c->value);
-
- if (c->repeat > 0)
- fprintf(f, "/%i", c->repeat);
+ if (!usec)
+ fprintf(f, "%0*i", space, c->value);
+ else if (c->value % USEC_PER_SEC == 0)
+ fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC));
+ else
+ fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC));
+
+ if (c->repeat > 0) {
+ if (!usec)
+ fprintf(f, "/%i", c->repeat);
+ else if (c->repeat % USEC_PER_SEC == 0)
+ fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC));
+ else
+ fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC));
+ }
if (c->next) {
fputc(',', f);
- format_chain(f, space, c->next);
+ format_chain(f, space, c->next, usec);
}
}
fputc(' ', f);
}
- format_chain(f, 4, c->year);
+ format_chain(f, 4, c->year, false);
fputc('-', f);
- format_chain(f, 2, c->month);
+ format_chain(f, 2, c->month, false);
fputc('-', f);
- format_chain(f, 2, c->day);
+ format_chain(f, 2, c->day, false);
fputc(' ', f);
- format_chain(f, 2, c->hour);
+ format_chain(f, 2, c->hour, false);
fputc(':', f);
- format_chain(f, 2, c->minute);
+ format_chain(f, 2, c->minute, false);
fputc(':', f);
- format_chain(f, 2, c->second);
+ format_chain(f, 2, c->microsecond, true);
if (c->utc)
fputs(" UTC", f);
}
}
-static int prepend_component(const char **p, CalendarComponent **c) {
- unsigned long value, repeat = 0;
- char *e = NULL, *ee = NULL;
- CalendarComponent *cc;
-
- assert(p);
- assert(c);
+static int parse_component_decimal(const char **p, bool usec, unsigned long *res) {
+ unsigned long value;
+ const char *e = NULL;
+ char *ee = NULL;
+ int r;
errno = 0;
- value = strtoul(*p, &e, 10);
+ value = strtoul(*p, &ee, 10);
if (errno > 0)
return -errno;
- if (e == *p)
+ if (ee == *p)
return -EINVAL;
if ((unsigned long) (int) value != value)
return -ERANGE;
+ e = ee;
- if (*e == '/') {
- repeat = strtoul(e+1, &ee, 10);
- if (errno > 0)
- return -errno;
- if (ee == e+1)
- return -EINVAL;
- if ((unsigned long) (int) repeat != repeat)
- return -ERANGE;
- if (repeat <= 0)
+ if (usec) {
+ if (value * USEC_PER_SEC / USEC_PER_SEC != value)
return -ERANGE;
- e = ee;
+ value *= USEC_PER_SEC;
+ if (*e == '.') {
+ unsigned add;
+
+ e++;
+ r = parse_fractional_part_u(&e, 6, &add);
+ if (r < 0)
+ return r;
+
+ if (add + value < value)
+ return -ERANGE;
+ value += add;
+ }
+ }
+
+ *p = e;
+ *res = value;
+
+ return 0;
+}
+
+static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
+ unsigned long value, repeat = 0;
+ CalendarComponent *cc;
+ int r;
+ const char *e;
+
+ assert(p);
+ assert(c);
+
+ e = *p;
+
+ r = parse_component_decimal(&e, usec, &value);
+ if (r < 0)
+ return r;
+
+ if (*e == '/') {
+ e++;
+ r = parse_component_decimal(&e, usec, &repeat);
+ if (r < 0)
+ return r;
+
+ if (repeat == 0)
+ return -ERANGE;
}
if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
if (*e ==',') {
*p += 1;
- return prepend_component(p, c);
+ return prepend_component(p, usec, c);
}
return 0;
}
-static int parse_chain(const char **p, CalendarComponent **c) {
+static int const_chain(int value, CalendarComponent **c) {
+ CalendarComponent *cc = NULL;
+
+ assert(c);
+
+ cc = new0(CalendarComponent, 1);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->value = value;
+ cc->repeat = 0;
+ cc->next = *c;
+
+ *c = cc;
+
+ return 0;
+}
+
+static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
const char *t;
CalendarComponent *cc = NULL;
int r;
t = *p;
if (t[0] == '*') {
+ if (usec) {
+ r = const_chain(0, c);
+ if (r < 0)
+ return r;
+ (*c)->repeat = USEC_PER_SEC;
+ } else
+ *c = NULL;
+
*p = t + 1;
- *c = NULL;
return 0;
}
- r = prepend_component(&t, &cc);
+ r = prepend_component(&t, usec, &cc);
if (r < 0) {
free_chain(cc);
return r;
return 0;
}
-static int const_chain(int value, CalendarComponent **c) {
- CalendarComponent *cc = NULL;
-
- assert(c);
-
- cc = new0(CalendarComponent, 1);
- if (!cc)
- return -ENOMEM;
-
- cc->value = value;
- cc->repeat = 0;
- cc->next = *c;
-
- *c = cc;
-
- return 0;
-}
-
static int parse_date(const char **p, CalendarSpec *c) {
const char *t;
int r;
if (*t == 0)
return 0;
- r = parse_chain(&t, &first);
+ r = parse_chain(&t, false, &first);
if (r < 0)
return r;
}
t++;
- r = parse_chain(&t, &second);
+ r = parse_chain(&t, false, &second);
if (r < 0) {
free_chain(first);
return r;
}
t++;
- r = parse_chain(&t, &third);
+ r = parse_chain(&t, false, &third);
if (r < 0) {
free_chain(first);
free_chain(second);
goto finish;
}
- r = parse_chain(&t, &h);
+ r = parse_chain(&t, false, &h);
if (r < 0)
goto fail;
}
t++;
- r = parse_chain(&t, &m);
+ r = parse_chain(&t, false, &m);
if (r < 0)
goto fail;
}
t++;
- r = parse_chain(&t, &s);
+ r = parse_chain(&t, true, &s);
if (r < 0)
goto fail;
*p = t;
c->hour = h;
c->minute = m;
- c->second = s;
+ c->microsecond = s;
+
return 0;
fail:
}
if (strcaseeq(p, "minutely")) {
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
return (weekdays_bits & (1 << k));
}
-static int find_next(const CalendarSpec *spec, struct tm *tm) {
+static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
struct tm c;
+ int tm_usec;
int r;
assert(spec);
assert(tm);
c = *tm;
+ tm_usec = *usec;
for (;;) {
/* Normalize the current date */
if (r > 0) {
c.tm_mon = 0;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
}
if (r < 0 || tm_out_of_bounds(&c, spec->utc))
return r;
if (r > 0) {
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
}
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_year ++;
c.tm_mon = 0;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
r = find_matching_component(spec->day, &c.tm_mday);
if (r > 0)
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mon ++;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
c.tm_mday++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
c.tm_min = c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mday ++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_hour ++;
- c.tm_min = c.tm_sec = 0;
+ c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
- r = find_matching_component(spec->second, &c.tm_sec);
+ c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
+ r = find_matching_component(spec->microsecond, &c.tm_sec);
+ tm_usec = c.tm_sec % USEC_PER_SEC;
+ c.tm_sec /= USEC_PER_SEC;
+
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_min ++;
- c.tm_sec = 0;
+ c.tm_sec = tm_usec = 0;
continue;
}
-
*tm = c;
+ *usec = tm_usec;
return 0;
}
}
struct tm tm;
time_t t;
int r;
+ usec_t tm_usec;
assert(spec);
assert(next);
- t = (time_t) (usec / USEC_PER_SEC) + 1;
+ usec++;
+ t = (time_t) (usec / USEC_PER_SEC);
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
+ tm_usec = usec % USEC_PER_SEC;
- r = find_next(spec, &tm);
+ r = find_next(spec, &tm, &tm_usec);
if (r < 0)
return r;
if (t == (time_t) -1)
return -EINVAL;
- *next = (usec_t) t * USEC_PER_SEC;
+ *next = (usec_t) t * USEC_PER_SEC + tm_usec;
return 0;
}