LCOV - code coverage report
Current view: top level - shared - calendarspec.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 722 822 87.8 %
Date: 2019-08-22 15:41:25 Functions: 29 29 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <alloca.h>
       4             : #include <ctype.h>
       5             : #include <errno.h>
       6             : #include <limits.h>
       7             : #include <stddef.h>
       8             : #include <stdio.h>
       9             : #include <stdlib.h>
      10             : #include <string.h>
      11             : #include <sys/mman.h>
      12             : #include <time.h>
      13             : 
      14             : #include "alloc-util.h"
      15             : #include "calendarspec.h"
      16             : #include "errno-util.h"
      17             : #include "fileio.h"
      18             : #include "macro.h"
      19             : #include "parse-util.h"
      20             : #include "process-util.h"
      21             : #include "sort-util.h"
      22             : #include "string-util.h"
      23             : #include "time-util.h"
      24             : 
      25             : #define BITS_WEEKDAYS 127
      26             : #define MIN_YEAR 1970
      27             : #define MAX_YEAR 2199
      28             : 
      29             : /* An arbitrary limit on the length of the chains of components. We don't want to
      30             :  * build a very long linked list, which would be slow to iterate over and might cause
      31             :  * our stack to overflow. It's unlikely that legitimate uses require more than a few
      32             :  * linked compenents anyway. */
      33             : #define CALENDARSPEC_COMPONENTS_MAX 240
      34             : 
      35        1128 : static void chain_free(CalendarComponent *c) {
      36             :         CalendarComponent *n;
      37             : 
      38        1892 :         while (c) {
      39         764 :                 n = c->next;
      40         764 :                 free(c);
      41         764 :                 c = n;
      42             :         }
      43        1128 : }
      44             : 
      45        1776 : DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarComponent*, chain_free);
      46             : 
      47         182 : CalendarSpec* calendar_spec_free(CalendarSpec *c) {
      48             : 
      49         182 :         if (!c)
      50           0 :                 return NULL;
      51             : 
      52         182 :         chain_free(c->year);
      53         182 :         chain_free(c->month);
      54         182 :         chain_free(c->day);
      55         182 :         chain_free(c->hour);
      56         182 :         chain_free(c->minute);
      57         182 :         chain_free(c->microsecond);
      58         182 :         free(c->timezone);
      59             : 
      60         182 :         return mfree(c);
      61             : }
      62             : 
      63          90 : static int component_compare(CalendarComponent * const *a, CalendarComponent * const *b) {
      64             :         int r;
      65             : 
      66          90 :         r = CMP((*a)->start, (*b)->start);
      67          90 :         if (r != 0)
      68          84 :                 return r;
      69             : 
      70           6 :         r = CMP((*a)->stop, (*b)->stop);
      71           6 :         if (r != 0)
      72           4 :                 return r;
      73             : 
      74           2 :         return CMP((*a)->repeat, (*b)->repeat);
      75             : }
      76             : 
      77         990 : static void normalize_chain(CalendarComponent **c) {
      78             :         CalendarComponent **b, *i, **j, *next;
      79         990 :         size_t n = 0, k;
      80             : 
      81         990 :         assert(c);
      82             : 
      83        1712 :         for (i = *c; i; i = i->next) {
      84         722 :                 n++;
      85             : 
      86             :                 /*
      87             :                  * While we're counting the chain, also normalize `stop`
      88             :                  * so the length of the range is a multiple of `repeat`
      89             :                  */
      90         722 :                 if (i->stop > i->start && i->repeat > 0)
      91          34 :                         i->stop -= (i->stop - i->start) % i->repeat;
      92             : 
      93             :         }
      94             : 
      95         990 :         if (n <= 1)
      96         965 :                 return;
      97             : 
      98          25 :         j = b = newa(CalendarComponent*, n);
      99          89 :         for (i = *c; i; i = i->next)
     100          64 :                 *(j++) = i;
     101             : 
     102          25 :         typesafe_qsort(b, n, component_compare);
     103             : 
     104          25 :         b[n-1]->next = NULL;
     105          25 :         next = b[n-1];
     106             : 
     107             :         /* Drop non-unique entries */
     108          64 :         for (k = n-1; k > 0; k--) {
     109          39 :                 if (component_compare(&b[k-1], &next) == 0) {
     110           1 :                         free(b[k-1]);
     111           1 :                         continue;
     112             :                 }
     113             : 
     114          38 :                 b[k-1]->next = next;
     115          38 :                 next = b[k-1];
     116             :         }
     117             : 
     118          25 :         *c = next;
     119             : }
     120             : 
     121         165 : static void fix_year(CalendarComponent *c) {
     122             :         /* Turns 12 → 2012, 89 → 1989 */
     123             : 
     124         221 :         while (c) {
     125          56 :                 if (c->start >= 0 && c->start < 70)
     126           4 :                         c->start += 2000;
     127             : 
     128          56 :                 if (c->stop >= 0 && c->stop < 70)
     129           2 :                         c->stop += 2000;
     130             : 
     131          56 :                 if (c->start >= 70 && c->start < 100)
     132           1 :                         c->start += 1900;
     133             : 
     134          56 :                 if (c->stop >= 70 && c->stop < 100)
     135           0 :                         c->stop += 1900;
     136             : 
     137          56 :                 c = c->next;
     138             :         }
     139         165 : }
     140             : 
     141         165 : int calendar_spec_normalize(CalendarSpec *c) {
     142         165 :         assert(c);
     143             : 
     144         165 :         if (streq_ptr(c->timezone, "UTC")) {
     145           0 :                 c->utc = true;
     146           0 :                 c->timezone = mfree(c->timezone);
     147             :         }
     148             : 
     149         165 :         if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
     150         123 :                 c->weekdays_bits = -1;
     151             : 
     152         165 :         if (c->end_of_month && !c->day)
     153           1 :                 c->end_of_month = false;
     154             : 
     155         165 :         fix_year(c->year);
     156             : 
     157         165 :         normalize_chain(&c->year);
     158         165 :         normalize_chain(&c->month);
     159         165 :         normalize_chain(&c->day);
     160         165 :         normalize_chain(&c->hour);
     161         165 :         normalize_chain(&c->minute);
     162         165 :         normalize_chain(&c->microsecond);
     163             : 
     164         165 :         return 0;
     165             : }
     166             : 
     167        1008 : _pure_ static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) {
     168        1008 :         assert(to >= from);
     169             : 
     170        1008 :         if (!c)
     171         306 :                 return true;
     172             : 
     173             :         /* Forbid dates more than 28 days from the end of the month */
     174         702 :         if (end_of_month)
     175          16 :                 to -= 3;
     176             : 
     177         702 :         if (c->start < from || c->start > to)
     178           2 :                 return false;
     179             : 
     180             :         /* Avoid overly large values that could cause overflow */
     181         700 :         if (c->repeat > to - from)
     182           1 :                 return false;
     183             : 
     184             :         /*
     185             :          * c->repeat must be short enough so at least one repetition may
     186             :          * occur before the end of the interval.  For dates scheduled
     187             :          * relative to the end of the month, c->start and c->stop
     188             :          * correspond to the Nth last day of the month.
     189             :          */
     190         699 :         if (c->stop >= 0) {
     191          36 :                 if (c->stop < from || c ->stop > to)
     192           1 :                         return false;
     193             : 
     194          35 :                 if (c->start + c->repeat > c->stop)
     195           4 :                         return false;
     196             :         } else {
     197         663 :                 if (end_of_month && c->start - c->repeat < from)
     198           1 :                         return false;
     199             : 
     200         662 :                 if (!end_of_month && c->start + c->repeat > to)
     201           1 :                         return false;
     202             :         }
     203             : 
     204         692 :         if (c->next)
     205          38 :                 return chain_valid(c->next, from, to, end_of_month);
     206             : 
     207         654 :         return true;
     208             : }
     209             : 
     210         165 : _pure_ bool calendar_spec_valid(CalendarSpec *c) {
     211         165 :         assert(c);
     212             : 
     213         165 :         if (c->weekdays_bits > BITS_WEEKDAYS)
     214           0 :                 return false;
     215             : 
     216         165 :         if (!chain_valid(c->year, MIN_YEAR, MAX_YEAR, false))
     217           0 :                 return false;
     218             : 
     219         165 :         if (!chain_valid(c->month, 1, 12, false))
     220           1 :                 return false;
     221             : 
     222         164 :         if (!chain_valid(c->day, 1, 31, c->end_of_month))
     223           3 :                 return false;
     224             : 
     225         161 :         if (!chain_valid(c->hour, 0, 23, false))
     226           2 :                 return false;
     227             : 
     228         159 :         if (!chain_valid(c->minute, 0, 59, false))
     229           3 :                 return false;
     230             : 
     231         156 :         if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1, false))
     232           1 :                 return false;
     233             : 
     234         155 :         return true;
     235             : }
     236             : 
     237          39 : static void format_weekdays(FILE *f, const CalendarSpec *c) {
     238             :         static const char *const days[] = {
     239             :                 "Mon",
     240             :                 "Tue",
     241             :                 "Wed",
     242             :                 "Thu",
     243             :                 "Fri",
     244             :                 "Sat",
     245             :                 "Sun"
     246             :         };
     247             : 
     248             :         int l, x;
     249          39 :         bool need_comma = false;
     250             : 
     251          39 :         assert(f);
     252          39 :         assert(c);
     253          39 :         assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
     254             : 
     255         312 :         for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) {
     256             : 
     257         273 :                 if (c->weekdays_bits & (1 << x)) {
     258             : 
     259          85 :                         if (l < 0) {
     260          49 :                                 if (need_comma)
     261          10 :                                         fputc(',', f);
     262             :                                 else
     263          39 :                                         need_comma = true;
     264             : 
     265          49 :                                 fputs(days[x], f);
     266          49 :                                 l = x;
     267             :                         }
     268             : 
     269         188 :                 } else if (l >= 0) {
     270             : 
     271          39 :                         if (x > l + 1) {
     272           8 :                                 fputs(x > l + 2 ? ".." : ",", f);
     273           8 :                                 fputs(days[x-1], f);
     274             :                         }
     275             : 
     276          39 :                         l = -1;
     277             :                 }
     278             :         }
     279             : 
     280          39 :         if (l >= 0 && x > l + 1) {
     281           8 :                 fputs(x > l + 2 ? ".." : ",", f);
     282           8 :                 fputs(days[x-1], f);
     283             :         }
     284          39 : }
     285             : 
     286         796 : static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
     287         796 :         int d = usec ? (int) USEC_PER_SEC : 1;
     288             : 
     289         796 :         assert(f);
     290             : 
     291         796 :         if (!c) {
     292         270 :                 fputc('*', f);
     293         270 :                 return;
     294             :         }
     295             : 
     296         526 :         if (usec && c->start == 0 && c->repeat == USEC_PER_SEC && !c->next) {
     297           2 :                 fputc('*', f);
     298           2 :                 return;
     299             :         }
     300             : 
     301         524 :         assert(c->start >= 0);
     302             : 
     303         524 :         fprintf(f, "%0*i", space, c->start / d);
     304         524 :         if (c->start % d > 0)
     305           5 :                 fprintf(f, ".%06i", c->start % d);
     306             : 
     307         524 :         if (c->stop > 0)
     308          28 :                 fprintf(f, "..%0*i", space, c->stop / d);
     309         524 :         if (c->stop % d > 0)
     310           2 :                 fprintf(f, ".%06i", c->stop % d);
     311             : 
     312         524 :         if (c->repeat > 0 && !(c->stop > 0 && c->repeat == d))
     313          12 :                 fprintf(f, "/%i", c->repeat / d);
     314         524 :         if (c->repeat % d > 0)
     315           2 :                 fprintf(f, ".%06i", c->repeat % d);
     316             : 
     317         524 :         if (c->next) {
     318          34 :                 fputc(',', f);
     319          34 :                 format_chain(f, space, c->next, usec);
     320             :         }
     321             : }
     322             : 
     323         127 : int calendar_spec_to_string(const CalendarSpec *c, char **p) {
     324         127 :         char *buf = NULL;
     325         127 :         size_t sz = 0;
     326             :         FILE *f;
     327             :         int r;
     328             : 
     329         127 :         assert(c);
     330         127 :         assert(p);
     331             : 
     332         127 :         f = open_memstream_unlocked(&buf, &sz);
     333         127 :         if (!f)
     334           0 :                 return -ENOMEM;
     335             : 
     336         127 :         if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
     337          39 :                 format_weekdays(f, c);
     338          39 :                 fputc(' ', f);
     339             :         }
     340             : 
     341         127 :         format_chain(f, 4, c->year, false);
     342         127 :         fputc('-', f);
     343         127 :         format_chain(f, 2, c->month, false);
     344         127 :         fputc(c->end_of_month ? '~' : '-', f);
     345         127 :         format_chain(f, 2, c->day, false);
     346         127 :         fputc(' ', f);
     347         127 :         format_chain(f, 2, c->hour, false);
     348         127 :         fputc(':', f);
     349         127 :         format_chain(f, 2, c->minute, false);
     350         127 :         fputc(':', f);
     351         127 :         format_chain(f, 2, c->microsecond, true);
     352             : 
     353         127 :         if (c->utc)
     354          12 :                 fputs(" UTC", f);
     355         115 :         else if (c->timezone != NULL) {
     356           4 :                 fputc(' ', f);
     357           4 :                 fputs(c->timezone, f);
     358         111 :         } else if (IN_SET(c->dst, 0, 1)) {
     359             : 
     360             :                 /* If daylight saving is explicitly on or off, let's show the used timezone. */
     361             : 
     362           1 :                 tzset();
     363             : 
     364           1 :                 if (!isempty(tzname[c->dst])) {
     365           1 :                         fputc(' ', f);
     366           1 :                         fputs(tzname[c->dst], f);
     367             :                 }
     368             :         }
     369             : 
     370         127 :         r = fflush_and_check(f);
     371         127 :         if (r < 0) {
     372           0 :                 free(buf);
     373           0 :                 fclose(f);
     374           0 :                 return r;
     375             :         }
     376             : 
     377         127 :         fclose(f);
     378             : 
     379         127 :         *p = buf;
     380         127 :         return 0;
     381             : }
     382             : 
     383         170 : static int parse_weekdays(const char **p, CalendarSpec *c) {
     384             :         static const struct {
     385             :                 const char *name;
     386             :                 const int nr;
     387             :         } day_nr[] = {
     388             :                 { "Monday",    0 },
     389             :                 { "Mon",       0 },
     390             :                 { "Tuesday",   1 },
     391             :                 { "Tue",       1 },
     392             :                 { "Wednesday", 2 },
     393             :                 { "Wed",       2 },
     394             :                 { "Thursday",  3 },
     395             :                 { "Thu",       3 },
     396             :                 { "Friday",    4 },
     397             :                 { "Fri",       4 },
     398             :                 { "Saturday",  5 },
     399             :                 { "Sat",       5 },
     400             :                 { "Sunday",    6 },
     401             :                 { "Sun",       6 }
     402             :         };
     403             : 
     404         170 :         int l = -1;
     405         170 :         bool first = true;
     406             : 
     407         170 :         assert(p);
     408         170 :         assert(*p);
     409         170 :         assert(c);
     410             : 
     411          36 :         for (;;) {
     412             :                 size_t i;
     413             : 
     414        2484 :                 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
     415             :                         size_t skip;
     416             : 
     417        2356 :                         if (!startswith_no_case(*p, day_nr[i].name))
     418        2278 :                                 continue;
     419             : 
     420          78 :                         skip = strlen(day_nr[i].name);
     421             : 
     422          78 :                         if (!IN_SET((*p)[skip], 0, '-', '.', ',', ' '))
     423           0 :                                 return -EINVAL;
     424             : 
     425          78 :                         c->weekdays_bits |= 1 << day_nr[i].nr;
     426             : 
     427          78 :                         if (l >= 0) {
     428             :                                 int j;
     429             : 
     430          12 :                                 if (l > day_nr[i].nr)
     431           0 :                                         return -EINVAL;
     432             : 
     433          28 :                                 for (j = l + 1; j < day_nr[i].nr; j++)
     434          16 :                                         c->weekdays_bits |= 1 << j;
     435             :                         }
     436             : 
     437          78 :                         *p += skip;
     438          78 :                         break;
     439             :                 }
     440             : 
     441             :                 /* Couldn't find this prefix, so let's assume the
     442             :                    weekday was not specified and let's continue with
     443             :                    the date */
     444         206 :                 if (i >= ELEMENTSOF(day_nr))
     445         128 :                         return first ? 0 : -EINVAL;
     446             : 
     447             :                 /* We reached the end of the string */
     448          78 :                 if (**p == 0)
     449           3 :                         return 0;
     450             : 
     451             :                 /* We reached the end of the weekday spec part */
     452          75 :                 if (**p == ' ') {
     453          35 :                         *p += strspn(*p, " ");
     454          35 :                         return 0;
     455             :                 }
     456             : 
     457          40 :                 if (**p == '.') {
     458          10 :                         if (l >= 0)
     459           0 :                                 return -EINVAL;
     460             : 
     461          10 :                         if ((*p)[1] != '.')
     462           0 :                                 return -EINVAL;
     463             : 
     464          10 :                         l = day_nr[i].nr;
     465          10 :                         *p += 2;
     466             : 
     467             :                 /* Support ranges with "-" for backwards compatibility */
     468          30 :                 } else if (**p == '-') {
     469           4 :                         if (l >= 0)
     470           0 :                                 return -EINVAL;
     471             : 
     472           4 :                         l = day_nr[i].nr;
     473           4 :                         *p += 1;
     474             : 
     475          26 :                 } else if (**p == ',') {
     476          26 :                         l = -1;
     477          26 :                         *p += 1;
     478             :                 }
     479             : 
     480             :                 /* Allow a trailing comma but not an open range */
     481          40 :                 if (IN_SET(**p, 0, ' ')) {
     482           4 :                         *p += strspn(*p, " ");
     483           4 :                         return l < 0 ? 0 : -EINVAL;
     484             :                 }
     485             : 
     486          36 :                 first = false;
     487             :         }
     488             : }
     489             : 
     490         679 : static int parse_one_number(const char *p, const char **e, unsigned long *ret) {
     491         679 :         char *ee = NULL;
     492             :         unsigned long value;
     493             : 
     494         679 :         errno = 0;
     495         679 :         value = strtoul(p, &ee, 10);
     496         679 :         if (errno > 0)
     497           0 :                 return -errno;
     498         679 :         if (ee == p)
     499           0 :                 return -EINVAL;
     500             : 
     501         679 :         *ret = value;
     502         679 :         *e = ee;
     503         679 :         return 0;
     504             : }
     505             : 
     506         679 : static int parse_component_decimal(const char **p, bool usec, int *res) {
     507             :         unsigned long value;
     508         679 :         const char *e = NULL;
     509             :         int r;
     510             : 
     511         679 :         if (!isdigit(**p))
     512           5 :                 return -EINVAL;
     513             : 
     514         674 :         r = parse_one_number(*p, &e, &value);
     515         674 :         if (r < 0)
     516           0 :                 return r;
     517             : 
     518         674 :         if (usec) {
     519         128 :                 if (value * USEC_PER_SEC / USEC_PER_SEC != value)
     520           1 :                         return -ERANGE;
     521             : 
     522         127 :                 value *= USEC_PER_SEC;
     523             : 
     524             :                 /* One "." is a decimal point, but ".." is a range separator */
     525         127 :                 if (e[0] == '.' && e[1] != '.') {
     526             :                         unsigned add;
     527             : 
     528          21 :                         e++;
     529          21 :                         r = parse_fractional_part_u(&e, 6, &add);
     530          21 :                         if (r < 0)
     531           0 :                                 return r;
     532             : 
     533          21 :                         if (add + value < value)
     534           0 :                                 return -ERANGE;
     535          21 :                         value += add;
     536             :                 }
     537             :         }
     538             : 
     539         673 :         if (value > INT_MAX)
     540           1 :                 return -ERANGE;
     541             : 
     542         672 :         *p = e;
     543         672 :         *res = value;
     544             : 
     545         672 :         return 0;
     546             : }
     547             : 
     548         158 : static int const_chain(int value, CalendarComponent **c) {
     549         158 :         CalendarComponent *cc = NULL;
     550             : 
     551         158 :         assert(c);
     552             : 
     553         158 :         cc = new(CalendarComponent, 1);
     554         158 :         if (!cc)
     555           0 :                 return -ENOMEM;
     556             : 
     557         158 :         *cc = (CalendarComponent) {
     558             :                 .start = value,
     559             :                 .stop = -1,
     560             :                 .repeat = 0,
     561         158 :                 .next = *c,
     562             :         };
     563             : 
     564         158 :         *c = cc;
     565             : 
     566         158 :         return 0;
     567             : }
     568             : 
     569           5 : static int calendarspec_from_time_t(CalendarSpec *c, time_t time) {
     570             :         _cleanup_(chain_freep) CalendarComponent
     571           5 :                 *year = NULL, *month = NULL, *day = NULL,
     572           5 :                 *hour = NULL, *minute = NULL, *us = NULL;
     573             :         struct tm tm;
     574             :         int r;
     575             : 
     576           5 :         if (!gmtime_r(&time, &tm))
     577           1 :                 return -ERANGE;
     578             : 
     579           4 :         if (tm.tm_year > INT_MAX - 1900)
     580           0 :                 return -ERANGE;
     581             : 
     582           4 :         r = const_chain(tm.tm_year + 1900, &year);
     583           4 :         if (r < 0)
     584           0 :                 return r;
     585             : 
     586           4 :         r = const_chain(tm.tm_mon + 1, &month);
     587           4 :         if (r < 0)
     588           0 :                 return r;
     589             : 
     590           4 :         r = const_chain(tm.tm_mday, &day);
     591           4 :         if (r < 0)
     592           0 :                 return r;
     593             : 
     594           4 :         r = const_chain(tm.tm_hour, &hour);
     595           4 :         if (r < 0)
     596           0 :                 return r;
     597             : 
     598           4 :         r = const_chain(tm.tm_min, &minute);
     599           4 :         if (r < 0)
     600           0 :                 return r;
     601             : 
     602           4 :         r = const_chain(tm.tm_sec * USEC_PER_SEC, &us);
     603           4 :         if (r < 0)
     604           0 :                 return r;
     605             : 
     606           4 :         c->utc = true;
     607           4 :         c->year = TAKE_PTR(year);
     608           4 :         c->month = TAKE_PTR(month);
     609           4 :         c->day = TAKE_PTR(day);
     610           4 :         c->hour = TAKE_PTR(hour);
     611           4 :         c->minute = TAKE_PTR(minute);
     612           4 :         c->microsecond = TAKE_PTR(us);
     613           4 :         return 0;
     614             : }
     615             : 
     616         617 : static int prepend_component(const char **p, bool usec, unsigned nesting, CalendarComponent **c) {
     617         617 :         int r, start, stop = -1, repeat = 0;
     618             :         CalendarComponent *cc;
     619         617 :         const char *e = *p;
     620             : 
     621         617 :         assert(p);
     622         617 :         assert(c);
     623             : 
     624         617 :         if (nesting > CALENDARSPEC_COMPONENTS_MAX)
     625           0 :                 return -ENOBUFS;
     626             : 
     627         617 :         r = parse_component_decimal(&e, usec, &start);
     628         617 :         if (r < 0)
     629           7 :                 return r;
     630             : 
     631         610 :         if (e[0] == '.' && e[1] == '.') {
     632          37 :                 e += 2;
     633          37 :                 r = parse_component_decimal(&e, usec, &stop);
     634          37 :                 if (r < 0)
     635           0 :                         return r;
     636             : 
     637          37 :                 repeat = usec ? USEC_PER_SEC : 1;
     638             :         }
     639             : 
     640         610 :         if (*e == '/') {
     641          25 :                 e++;
     642          25 :                 r = parse_component_decimal(&e, usec, &repeat);
     643          25 :                 if (r < 0)
     644           0 :                         return r;
     645             : 
     646          25 :                 if (repeat == 0)
     647           1 :                         return -ERANGE;
     648             :         }
     649             : 
     650         609 :         if (!IN_SET(*e, 0, ' ', ',', '-', '~', ':'))
     651           2 :                 return -EINVAL;
     652             : 
     653         607 :         cc = new(CalendarComponent, 1);
     654         607 :         if (!cc)
     655           0 :                 return -ENOMEM;
     656             : 
     657        1214 :         *cc = (CalendarComponent) {
     658             :                 .start = start,
     659             :                 .stop = stop,
     660             :                 .repeat = repeat,
     661         607 :                 .next = *c,
     662             :         };
     663             : 
     664         607 :         *p = e;
     665         607 :         *c = cc;
     666             : 
     667         607 :         if (*e ==',') {
     668          39 :                 *p += 1;
     669          39 :                 return prepend_component(p, usec, nesting + 1, c);
     670             :         }
     671             : 
     672         568 :         return 0;
     673             : }
     674             : 
     675         771 : static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
     676         771 :         _cleanup_(chain_freep) CalendarComponent *cc = NULL;
     677             :         const char *t;
     678             :         int r;
     679             : 
     680         771 :         assert(p);
     681         771 :         assert(c);
     682             : 
     683         771 :         t = *p;
     684             : 
     685         771 :         if (t[0] == '*') {
     686         193 :                 if (usec) {
     687           2 :                         r = const_chain(0, c);
     688           2 :                         if (r < 0)
     689           0 :                                 return r;
     690           2 :                         (*c)->repeat = USEC_PER_SEC;
     691             :                 } else
     692         191 :                         *c = NULL;
     693             : 
     694         193 :                 *p = t + 1;
     695         193 :                 return 0;
     696             :         }
     697             : 
     698         578 :         r = prepend_component(&t, usec, 0, &cc);
     699         578 :         if (r < 0)
     700          10 :                 return r;
     701             : 
     702         568 :         *p = t;
     703         568 :         *c = TAKE_PTR(cc);
     704         568 :         return 0;
     705             : }
     706             : 
     707         168 : static int parse_date(const char **p, CalendarSpec *c) {
     708         168 :         _cleanup_(chain_freep) CalendarComponent *first = NULL, *second = NULL, *third = NULL;
     709             :         const char *t;
     710             :         int r;
     711             : 
     712         168 :         assert(p);
     713         168 :         assert(*p);
     714         168 :         assert(c);
     715             : 
     716         168 :         t = *p;
     717             : 
     718         168 :         if (*t == 0)
     719           4 :                 return 0;
     720             : 
     721             :         /* @TIMESTAMP — UNIX time in seconds since the epoch */
     722         164 :         if (*t == '@') {
     723             :                 unsigned long value;
     724             :                 time_t time;
     725             : 
     726           5 :                 r = parse_one_number(t + 1, &t, &value);
     727           5 :                 if (r < 0)
     728           0 :                         return r;
     729             : 
     730           5 :                 time = value;
     731           5 :                 if ((unsigned long) time != value)
     732           0 :                         return -ERANGE;
     733             : 
     734           5 :                 r = calendarspec_from_time_t(c, time);
     735           5 :                 if (r < 0)
     736           1 :                         return r;
     737             : 
     738           4 :                 *p = t;
     739           4 :                 return 1; /* finito, don't parse H:M:S after that */
     740             :         }
     741             : 
     742         159 :         r = parse_chain(&t, false, &first);
     743         159 :         if (r < 0)
     744           3 :                 return r;
     745             : 
     746             :         /* Already the end? A ':' as separator? In that case this was a time, not a date */
     747         156 :         if (IN_SET(*t, 0, ':'))
     748          29 :                 return 0;
     749             : 
     750         127 :         if (*t == '~')
     751           4 :                 c->end_of_month = true;
     752         123 :         else if (*t != '-')
     753           0 :                 return -EINVAL;
     754             : 
     755         127 :         t++;
     756         127 :         r = parse_chain(&t, false, &second);
     757         127 :         if (r < 0)
     758           1 :                 return r;
     759             : 
     760             :         /* Got two parts, hence it's month and day */
     761         126 :         if (IN_SET(*t, 0, ' ')) {
     762          12 :                 *p = t + strspn(t, " ");
     763          12 :                 c->month = TAKE_PTR(first);
     764          12 :                 c->day = TAKE_PTR(second);
     765          12 :                 return 0;
     766         114 :         } else if (c->end_of_month)
     767           1 :                 return -EINVAL;
     768             : 
     769         113 :         if (*t == '~')
     770          10 :                 c->end_of_month = true;
     771         103 :         else if (*t != '-')
     772           0 :                 return -EINVAL;
     773             : 
     774         113 :         t++;
     775         113 :         r = parse_chain(&t, false, &third);
     776         113 :         if (r < 0)
     777           1 :                 return r;
     778             : 
     779         112 :         if (!IN_SET(*t, 0, ' '))
     780           0 :                 return -EINVAL;
     781             : 
     782             :         /* Got three parts, hence it is year, month and day */
     783         112 :         *p = t + strspn(t, " ");
     784         112 :         c->year = TAKE_PTR(first);
     785         112 :         c->month = TAKE_PTR(second);
     786         112 :         c->day = TAKE_PTR(third);
     787         112 :         return 0;
     788             : }
     789             : 
     790         157 : static int parse_calendar_time(const char **p, CalendarSpec *c) {
     791         157 :         _cleanup_(chain_freep) CalendarComponent *h = NULL, *m = NULL, *s = NULL;
     792             :         const char *t;
     793             :         int r;
     794             : 
     795         157 :         assert(p);
     796         157 :         assert(*p);
     797         157 :         assert(c);
     798             : 
     799         157 :         t = *p;
     800             : 
     801             :         /* If no time is specified at all, then this means 00:00:00 */
     802         157 :         if (*t == 0)
     803          25 :                 goto null_hour;
     804             : 
     805         132 :         r = parse_chain(&t, false, &h);
     806         132 :         if (r < 0)
     807           0 :                 return r;
     808             : 
     809         132 :         if (*t != ':')
     810           1 :                 return -EINVAL;
     811             : 
     812         131 :         t++;
     813         131 :         r = parse_chain(&t, false, &m);
     814         131 :         if (r < 0)
     815           2 :                 return r;
     816             : 
     817             :         /* Already at the end? Then it's hours and minutes, and seconds are 0 */
     818         129 :         if (*t == 0)
     819          20 :                 goto null_second;
     820             : 
     821         109 :         if (*t != ':')
     822           0 :                 return -EINVAL;
     823             : 
     824         109 :         t++;
     825         109 :         r = parse_chain(&t, true, &s);
     826         109 :         if (r < 0)
     827           3 :                 return r;
     828             : 
     829             :         /* At the end? Then it's hours, minutes and seconds */
     830         106 :         if (*t == 0)
     831         106 :                 goto finish;
     832             : 
     833           0 :         return -EINVAL;
     834             : 
     835          25 : null_hour:
     836          25 :         r = const_chain(0, &h);
     837          25 :         if (r < 0)
     838           0 :                 return r;
     839             : 
     840          25 :         r = const_chain(0, &m);
     841          25 :         if (r < 0)
     842           0 :                 return r;
     843             : 
     844          25 : null_second:
     845          45 :         r = const_chain(0, &s);
     846          45 :         if (r < 0)
     847           0 :                 return r;
     848             : 
     849          45 : finish:
     850         151 :         *p = t;
     851         151 :         c->hour = TAKE_PTR(h);
     852         151 :         c->minute = TAKE_PTR(m);
     853         151 :         c->microsecond = TAKE_PTR(s);
     854             : 
     855         151 :         return 0;
     856             : }
     857             : 
     858         182 : int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
     859             :         const char *utc;
     860         182 :         _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
     861         182 :         _cleanup_free_ char *p_tmp = NULL;
     862             :         int r;
     863             : 
     864         182 :         assert(p);
     865             : 
     866         182 :         c = new(CalendarSpec, 1);
     867         182 :         if (!c)
     868           0 :                 return -ENOMEM;
     869             : 
     870         182 :         *c = (CalendarSpec) {
     871             :                 .dst = -1,
     872             :                 .timezone = NULL,
     873             :         };
     874             : 
     875         182 :         utc = endswith_no_case(p, " UTC");
     876         182 :         if (utc) {
     877          23 :                 c->utc = true;
     878          23 :                 p = p_tmp = strndup(p, utc - p);
     879          23 :                 if (!p)
     880           0 :                         return -ENOMEM;
     881             :         } else {
     882         159 :                 const char *e = NULL;
     883             :                 int j;
     884             : 
     885         159 :                 tzset();
     886             : 
     887             :                 /* Check if the local timezone was specified? */
     888         476 :                 for (j = 0; j <= 1; j++) {
     889         318 :                         if (isempty(tzname[j]))
     890           0 :                                 continue;
     891             : 
     892         318 :                         e = endswith_no_case(p, tzname[j]);
     893         318 :                         if (!e)
     894         317 :                                 continue;
     895           1 :                         if (e == p)
     896           0 :                                 continue;
     897           1 :                         if (e[-1] != ' ')
     898           0 :                                 continue;
     899             : 
     900           1 :                         break;
     901             :                 }
     902             : 
     903             :                 /* Found one of the two timezones specified? */
     904         159 :                 if (IN_SET(j, 0, 1)) {
     905           1 :                         p = p_tmp = strndup(p, e - p - 1);
     906           1 :                         if (!p)
     907           0 :                                 return -ENOMEM;
     908             : 
     909           1 :                         c->dst = j;
     910             :                 } else {
     911             :                         const char *last_space;
     912             : 
     913         158 :                         last_space = strrchr(p, ' ');
     914         158 :                         if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG)) {
     915          11 :                                 c->timezone = strdup(last_space + 1);
     916          11 :                                 if (!c->timezone)
     917           0 :                                         return -ENOMEM;
     918             : 
     919          11 :                                 p = p_tmp = strndup(p, last_space - p);
     920          11 :                                 if (!p)
     921           0 :                                         return -ENOMEM;
     922             :                         }
     923             :                 }
     924             :         }
     925             : 
     926         182 :         if (isempty(p))
     927           2 :                 return -EINVAL;
     928             : 
     929         180 :         if (strcaseeq(p, "minutely")) {
     930           1 :                 r = const_chain(0, &c->microsecond);
     931           1 :                 if (r < 0)
     932           0 :                         return r;
     933             : 
     934         179 :         } else if (strcaseeq(p, "hourly")) {
     935           2 :                 r = const_chain(0, &c->minute);
     936           2 :                 if (r < 0)
     937           0 :                         return r;
     938           2 :                 r = const_chain(0, &c->microsecond);
     939           2 :                 if (r < 0)
     940           0 :                         return r;
     941             : 
     942         177 :         } else if (strcaseeq(p, "daily")) {
     943           1 :                 r = const_chain(0, &c->hour);
     944           1 :                 if (r < 0)
     945           0 :                         return r;
     946           1 :                 r = const_chain(0, &c->minute);
     947           1 :                 if (r < 0)
     948           0 :                         return r;
     949           1 :                 r = const_chain(0, &c->microsecond);
     950           1 :                 if (r < 0)
     951           0 :                         return r;
     952             : 
     953         176 :         } else if (strcaseeq(p, "monthly")) {
     954           1 :                 r = const_chain(1, &c->day);
     955           1 :                 if (r < 0)
     956           0 :                         return r;
     957           1 :                 r = const_chain(0, &c->hour);
     958           1 :                 if (r < 0)
     959           0 :                         return r;
     960           1 :                 r = const_chain(0, &c->minute);
     961           1 :                 if (r < 0)
     962           0 :                         return r;
     963           1 :                 r = const_chain(0, &c->microsecond);
     964           1 :                 if (r < 0)
     965           0 :                         return r;
     966             : 
     967         175 :         } else if (strcaseeq(p, "annually") ||
     968         174 :                    strcaseeq(p, "yearly") ||
     969         174 :                    strcaseeq(p, "anually") /* backwards compatibility */ ) {
     970             : 
     971           1 :                 r = const_chain(1, &c->month);
     972           1 :                 if (r < 0)
     973           0 :                         return r;
     974           1 :                 r = const_chain(1, &c->day);
     975           1 :                 if (r < 0)
     976           0 :                         return r;
     977           1 :                 r = const_chain(0, &c->hour);
     978           1 :                 if (r < 0)
     979           0 :                         return r;
     980           1 :                 r = const_chain(0, &c->minute);
     981           1 :                 if (r < 0)
     982           0 :                         return r;
     983           1 :                 r = const_chain(0, &c->microsecond);
     984           1 :                 if (r < 0)
     985           0 :                         return r;
     986             : 
     987         174 :         } else if (strcaseeq(p, "weekly")) {
     988             : 
     989           2 :                 c->weekdays_bits = 1;
     990             : 
     991           2 :                 r = const_chain(0, &c->hour);
     992           2 :                 if (r < 0)
     993           0 :                         return r;
     994           2 :                 r = const_chain(0, &c->minute);
     995           2 :                 if (r < 0)
     996           0 :                         return r;
     997           2 :                 r = const_chain(0, &c->microsecond);
     998           2 :                 if (r < 0)
     999           0 :                         return r;
    1000             : 
    1001         172 :         } else if (strcaseeq(p, "quarterly")) {
    1002             : 
    1003           1 :                 r = const_chain(1, &c->month);
    1004           1 :                 if (r < 0)
    1005           0 :                         return r;
    1006           1 :                 r = const_chain(4, &c->month);
    1007           1 :                 if (r < 0)
    1008           0 :                         return r;
    1009           1 :                 r = const_chain(7, &c->month);
    1010           1 :                 if (r < 0)
    1011           0 :                         return r;
    1012           1 :                 r = const_chain(10, &c->month);
    1013           1 :                 if (r < 0)
    1014           0 :                         return r;
    1015           1 :                 r = const_chain(1, &c->day);
    1016           1 :                 if (r < 0)
    1017           0 :                         return r;
    1018           1 :                 r = const_chain(0, &c->hour);
    1019           1 :                 if (r < 0)
    1020           0 :                         return r;
    1021           1 :                 r = const_chain(0, &c->minute);
    1022           1 :                 if (r < 0)
    1023           0 :                         return r;
    1024           1 :                 r = const_chain(0, &c->microsecond);
    1025           1 :                 if (r < 0)
    1026           0 :                         return r;
    1027             : 
    1028         171 :         } else if (strcaseeq(p, "biannually") ||
    1029         171 :                    strcaseeq(p, "bi-annually") ||
    1030         171 :                    strcaseeq(p, "semiannually") ||
    1031         171 :                    strcaseeq(p, "semi-annually")) {
    1032             : 
    1033           1 :                 r = const_chain(1, &c->month);
    1034           1 :                 if (r < 0)
    1035           0 :                         return r;
    1036           1 :                 r = const_chain(7, &c->month);
    1037           1 :                 if (r < 0)
    1038           0 :                         return r;
    1039           1 :                 r = const_chain(1, &c->day);
    1040           1 :                 if (r < 0)
    1041           0 :                         return r;
    1042           1 :                 r = const_chain(0, &c->hour);
    1043           1 :                 if (r < 0)
    1044           0 :                         return r;
    1045           1 :                 r = const_chain(0, &c->minute);
    1046           1 :                 if (r < 0)
    1047           0 :                         return r;
    1048           1 :                 r = const_chain(0, &c->microsecond);
    1049           1 :                 if (r < 0)
    1050           0 :                         return r;
    1051             : 
    1052             :         } else {
    1053         170 :                 r = parse_weekdays(&p, c);
    1054         170 :                 if (r < 0)
    1055           2 :                         return r;
    1056             : 
    1057         168 :                 r = parse_date(&p, c);
    1058         168 :                 if (r < 0)
    1059           7 :                         return r;
    1060             : 
    1061         161 :                 if (r == 0) {
    1062         157 :                         r = parse_calendar_time(&p, c);
    1063         157 :                         if (r < 0)
    1064           6 :                                 return r;
    1065             :                 }
    1066             : 
    1067         155 :                 if (*p != 0)
    1068           0 :                         return -EINVAL;
    1069             :         }
    1070             : 
    1071         165 :         r = calendar_spec_normalize(c);
    1072         165 :         if (r < 0)
    1073           0 :                 return r;
    1074             : 
    1075         165 :         if (!calendar_spec_valid(c))
    1076          10 :                 return -EINVAL;
    1077             : 
    1078         155 :         if (spec)
    1079         155 :                 *spec = TAKE_PTR(c);
    1080         155 :         return 0;
    1081             : }
    1082             : 
    1083          32 : static int find_end_of_month(struct tm *tm, bool utc, int day) {
    1084          32 :         struct tm t = *tm;
    1085             : 
    1086          32 :         t.tm_mon++;
    1087          32 :         t.tm_mday = 1 - day;
    1088             : 
    1089          32 :         if (mktime_or_timegm(&t, utc) < 0 ||
    1090          32 :             t.tm_mon != tm->tm_mon)
    1091          10 :                 return -1;
    1092             : 
    1093          22 :         return t.tm_mday;
    1094             : }
    1095             : 
    1096        1936 : static int find_matching_component(const CalendarSpec *spec, const CalendarComponent *c,
    1097             :                                    struct tm *tm, int *val) {
    1098        1936 :         const CalendarComponent *p = c;
    1099        1936 :         int start, stop, d = -1;
    1100        1936 :         bool d_set = false;
    1101             :         int r;
    1102             : 
    1103        1936 :         assert(val);
    1104             : 
    1105        1936 :         if (!c)
    1106         847 :                 return 0;
    1107             : 
    1108        2209 :         while (c) {
    1109        1120 :                 start = c->start;
    1110        1120 :                 stop = c->stop;
    1111             : 
    1112        1120 :                 if (spec->end_of_month && p == spec->day) {
    1113          16 :                         start = find_end_of_month(tm, spec->utc, start);
    1114          16 :                         stop = find_end_of_month(tm, spec->utc, stop);
    1115             : 
    1116          16 :                         if (stop > 0)
    1117           6 :                                 SWAP_TWO(start, stop);
    1118             :                 }
    1119             : 
    1120        1120 :                 if (start >= *val) {
    1121             : 
    1122         785 :                         if (!d_set || start < d) {
    1123         771 :                                 d = start;
    1124         771 :                                 d_set = true;
    1125             :                         }
    1126             : 
    1127         335 :                 } else if (c->repeat > 0) {
    1128             :                         int k;
    1129             : 
    1130          26 :                         k = start + c->repeat * DIV_ROUND_UP(*val - start, c->repeat);
    1131             : 
    1132          26 :                         if ((!d_set || k < d) && (stop < 0 || k <= stop)) {
    1133          20 :                                 d = k;
    1134          20 :                                 d_set = true;
    1135             :                         }
    1136             :                 }
    1137             : 
    1138        1120 :                 c = c->next;
    1139             :         }
    1140             : 
    1141        1089 :         if (!d_set)
    1142         300 :                 return -ENOENT;
    1143             : 
    1144         789 :         r = *val != d;
    1145         789 :         *val = d;
    1146         789 :         return r;
    1147             : }
    1148             : 
    1149        1636 : static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
    1150             :         struct tm t;
    1151        1636 :         assert(tm);
    1152             : 
    1153        1636 :         t = *tm;
    1154             : 
    1155        1636 :         if (mktime_or_timegm(&t, utc) < 0)
    1156           0 :                 return true;
    1157             : 
    1158             :         /*
    1159             :          * Set an upper bound on the year so impossible dates like "*-02-31"
    1160             :          * don't cause find_next() to loop forever. tm_year contains years
    1161             :          * since 1900, so adjust it accordingly.
    1162             :          */
    1163        1636 :         if (tm->tm_year + 1900 > MAX_YEAR)
    1164           1 :                 return true;
    1165             : 
    1166             :         /* Did any normalization take place? If so, it was out of bounds before */
    1167             :         return
    1168        3270 :                 t.tm_year != tm->tm_year ||
    1169        1635 :                 t.tm_mon != tm->tm_mon ||
    1170        1405 :                 t.tm_mday != tm->tm_mday ||
    1171        1405 :                 t.tm_hour != tm->tm_hour ||
    1172        4674 :                 t.tm_min != tm->tm_min ||
    1173        1404 :                 t.tm_sec != tm->tm_sec;
    1174             : }
    1175             : 
    1176         149 : static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
    1177             :         struct tm t;
    1178             :         int k;
    1179             : 
    1180         149 :         if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
    1181          76 :                 return true;
    1182             : 
    1183          73 :         t = *tm;
    1184          73 :         if (mktime_or_timegm(&t, utc) < 0)
    1185           0 :                 return false;
    1186             : 
    1187          73 :         k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
    1188          73 :         return (weekdays_bits & (1 << k));
    1189             : }
    1190             : 
    1191          83 : static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
    1192             :         struct tm c;
    1193             :         int tm_usec;
    1194             :         int r;
    1195             : 
    1196             :         /* Returns -ENOENT if the expression is not going to elapse anymore */
    1197             : 
    1198          83 :         assert(spec);
    1199          83 :         assert(tm);
    1200             : 
    1201          83 :         c = *tm;
    1202          83 :         tm_usec = *usec;
    1203             : 
    1204             :         for (;;) {
    1205             :                 /* Normalize the current date */
    1206         652 :                 (void) mktime_or_timegm(&c, spec->utc);
    1207         652 :                 c.tm_isdst = spec->dst;
    1208             : 
    1209         652 :                 c.tm_year += 1900;
    1210         652 :                 r = find_matching_component(spec, spec->year, &c, &c.tm_year);
    1211         652 :                 c.tm_year -= 1900;
    1212             : 
    1213         652 :                 if (r > 0) {
    1214          13 :                         c.tm_mon = 0;
    1215          13 :                         c.tm_mday = 1;
    1216          13 :                         c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
    1217             :                 }
    1218         652 :                 if (r < 0)
    1219          15 :                         return r;
    1220         637 :                 if (tm_out_of_bounds(&c, spec->utc))
    1221           1 :                         return -ENOENT;
    1222             : 
    1223         636 :                 c.tm_mon += 1;
    1224         636 :                 r = find_matching_component(spec, spec->month, &c, &c.tm_mon);
    1225         636 :                 c.tm_mon -= 1;
    1226             : 
    1227         636 :                 if (r > 0) {
    1228         252 :                         c.tm_mday = 1;
    1229         252 :                         c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
    1230             :                 }
    1231         636 :                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
    1232         236 :                         c.tm_year++;
    1233         236 :                         c.tm_mon = 0;
    1234         236 :                         c.tm_mday = 1;
    1235         236 :                         c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
    1236         236 :                         continue;
    1237             :                 }
    1238             : 
    1239         400 :                 r = find_matching_component(spec, spec->day, &c, &c.tm_mday);
    1240         400 :                 if (r > 0)
    1241         253 :                         c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
    1242         400 :                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
    1243         251 :                         c.tm_mon++;
    1244         251 :                         c.tm_mday = 1;
    1245         251 :                         c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
    1246         251 :                         continue;
    1247             :                 }
    1248             : 
    1249         149 :                 if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
    1250          53 :                         c.tm_mday++;
    1251          53 :                         c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
    1252          53 :                         continue;
    1253             :                 }
    1254             : 
    1255          96 :                 r = find_matching_component(spec, spec->hour, &c, &c.tm_hour);
    1256          96 :                 if (r > 0)
    1257          25 :                         c.tm_min = c.tm_sec = tm_usec = 0;
    1258          96 :                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
    1259          17 :                         c.tm_mday++;
    1260          17 :                         c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
    1261          17 :                         continue;
    1262             :                 }
    1263             : 
    1264          79 :                 r = find_matching_component(spec, spec->minute, &c, &c.tm_min);
    1265          79 :                 if (r > 0)
    1266          22 :                         c.tm_sec = tm_usec = 0;
    1267          79 :                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
    1268           6 :                         c.tm_hour++;
    1269           6 :                         c.tm_min = c.tm_sec = tm_usec = 0;
    1270           6 :                         continue;
    1271             :                 }
    1272             : 
    1273          73 :                 c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
    1274          73 :                 r = find_matching_component(spec, spec->microsecond, &c, &c.tm_sec);
    1275          73 :                 tm_usec = c.tm_sec % USEC_PER_SEC;
    1276          73 :                 c.tm_sec /= USEC_PER_SEC;
    1277             : 
    1278          73 :                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
    1279           6 :                         c.tm_min++;
    1280           6 :                         c.tm_sec = tm_usec = 0;
    1281           6 :                         continue;
    1282             :                 }
    1283             : 
    1284          67 :                 *tm = c;
    1285          67 :                 *usec = tm_usec;
    1286          67 :                 return 0;
    1287             :         }
    1288             : }
    1289             : 
    1290          83 : static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) {
    1291             :         struct tm tm;
    1292             :         time_t t;
    1293             :         int r;
    1294             :         usec_t tm_usec;
    1295             : 
    1296          83 :         assert(spec);
    1297             : 
    1298          83 :         if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
    1299           0 :                 return -EINVAL;
    1300             : 
    1301          83 :         usec++;
    1302          83 :         t = (time_t) (usec / USEC_PER_SEC);
    1303          83 :         assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
    1304          83 :         tm_usec = usec % USEC_PER_SEC;
    1305             : 
    1306          83 :         r = find_next(spec, &tm, &tm_usec);
    1307          83 :         if (r < 0)
    1308          16 :                 return r;
    1309             : 
    1310          67 :         t = mktime_or_timegm(&tm, spec->utc);
    1311          67 :         if (t < 0)
    1312           0 :                 return -EINVAL;
    1313             : 
    1314          67 :         if (ret_next)
    1315          67 :                 *ret_next = (usec_t) t * USEC_PER_SEC + tm_usec;
    1316             : 
    1317          67 :         return 0;
    1318             : }
    1319             : 
    1320             : typedef struct SpecNextResult {
    1321             :         usec_t next;
    1322             :         int return_value;
    1323             : } SpecNextResult;
    1324             : 
    1325          92 : int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) {
    1326             :         SpecNextResult *shared, tmp;
    1327             :         int r;
    1328             : 
    1329          92 :         assert(spec);
    1330             : 
    1331          92 :         if (isempty(spec->timezone))
    1332          83 :                 return calendar_spec_next_usec_impl(spec, usec, ret_next);
    1333             : 
    1334           9 :         shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    1335           9 :         if (shared == MAP_FAILED)
    1336           0 :                 return negative_errno();
    1337             : 
    1338           9 :         r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
    1339           9 :         if (r < 0) {
    1340           0 :                 (void) munmap(shared, sizeof *shared);
    1341           0 :                 return r;
    1342             :         }
    1343           9 :         if (r == 0) {
    1344           0 :                 if (setenv("TZ", spec->timezone, 1) != 0) {
    1345           0 :                         shared->return_value = negative_errno();
    1346           0 :                         _exit(EXIT_FAILURE);
    1347             :                 }
    1348             : 
    1349           0 :                 tzset();
    1350             : 
    1351           0 :                 shared->return_value = calendar_spec_next_usec_impl(spec, usec, &shared->next);
    1352             : 
    1353           0 :                 _exit(EXIT_SUCCESS);
    1354             :         }
    1355             : 
    1356           9 :         tmp = *shared;
    1357           9 :         if (munmap(shared, sizeof *shared) < 0)
    1358           0 :                 return negative_errno();
    1359             : 
    1360           9 :         if (tmp.return_value == 0 && ret_next)
    1361           6 :                 *ret_next = tmp.next;
    1362             : 
    1363           9 :         return tmp.return_value;
    1364             : }

Generated by: LCOV version 1.14