chiark / gitweb /
Fix check_loopback()
[elogind.git] / src / shared / calendarspec.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "calendarspec.h"
26
27 #define BITS_WEEKDAYS   127
28
29 static void free_chain(CalendarComponent *c) {
30         CalendarComponent *n;
31
32         while (c) {
33                 n = c->next;
34                 free(c);
35                 c = n;
36         }
37 }
38
39 void calendar_spec_free(CalendarSpec *c) {
40
41         if (!c)
42                 return;
43
44         free_chain(c->year);
45         free_chain(c->month);
46         free_chain(c->day);
47         free_chain(c->hour);
48         free_chain(c->minute);
49         free_chain(c->second);
50
51         free(c);
52 }
53
54 static int component_compare(const void *_a, const void *_b) {
55         CalendarComponent * const *a = _a, * const *b = _b;
56
57         if ((*a)->value < (*b)->value)
58                 return -1;
59         if ((*a)->value > (*b)->value)
60                 return 1;
61
62         if ((*a)->repeat < (*b)->repeat)
63                 return -1;
64         if ((*a)->repeat > (*b)->repeat)
65                 return 1;
66
67         return 0;
68 }
69
70 static void sort_chain(CalendarComponent **c) {
71         unsigned n = 0, k;
72         CalendarComponent **b, *i, **j, *next;
73
74         assert(c);
75
76         for (i = *c; i; i = i->next)
77                 n++;
78
79         if (n <= 1)
80                 return;
81
82         j = b = alloca(sizeof(CalendarComponent*) * n);
83         for (i = *c; i; i = i->next)
84                 *(j++) = i;
85
86         qsort(b, n, sizeof(CalendarComponent*), component_compare);
87
88         b[n-1]->next = NULL;
89         next = b[n-1];
90
91         /* Drop non-unique entries */
92         for (k = n-1; k > 0; k--) {
93                 if (b[k-1]->value == next->value &&
94                     b[k-1]->repeat == next->repeat) {
95                         free(b[k-1]);
96                         continue;
97                 }
98
99                 b[k-1]->next = next;
100                 next = b[k-1];
101         }
102
103         *c = next;
104 }
105
106 static void fix_year(CalendarComponent *c) {
107         /* Turns 12 → 2012, 89 → 1989 */
108
109         while(c) {
110                 CalendarComponent *n = c->next;
111
112                 if (c->value >= 0 && c->value < 70)
113                         c->value += 2000;
114
115                 if (c->value >= 70 && c->value < 100)
116                         c->value += 1900;
117
118                 c = n;
119         }
120 }
121
122 int calendar_spec_normalize(CalendarSpec *c) {
123         assert(c);
124
125         if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
126                 c->weekdays_bits = -1;
127
128         fix_year(c->year);
129
130         sort_chain(&c->year);
131         sort_chain(&c->month);
132         sort_chain(&c->day);
133         sort_chain(&c->hour);
134         sort_chain(&c->minute);
135         sort_chain(&c->second);
136
137         return 0;
138 }
139
140 _pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
141         if (!c)
142                 return true;
143
144         if (c->value < from || c->value > to)
145                 return false;
146
147         if (c->value + c->repeat > to)
148                 return false;
149
150         if (c->next)
151                 return chain_valid(c->next, from, to);
152
153         return true;
154 }
155
156 _pure_ bool calendar_spec_valid(CalendarSpec *c) {
157         assert(c);
158
159         if (c->weekdays_bits > BITS_WEEKDAYS)
160                 return false;
161
162         if (!chain_valid(c->year, 1970, 2199))
163                 return false;
164
165         if (!chain_valid(c->month, 1, 12))
166                 return false;
167
168         if (!chain_valid(c->day, 1, 31))
169                 return false;
170
171         if (!chain_valid(c->hour, 0, 23))
172                 return false;
173
174         if (!chain_valid(c->minute, 0, 59))
175                 return false;
176
177         if (!chain_valid(c->second, 0, 59))
178                 return false;
179
180         return true;
181 }
182
183 static void format_weekdays(FILE *f, const CalendarSpec *c) {
184         static const char *const days[] = {
185                 "Mon",
186                 "Tue",
187                 "Wed",
188                 "Thu",
189                 "Fri",
190                 "Sat",
191                 "Sun"
192         };
193
194         int l, x;
195         bool need_colon = false;
196
197         assert(f);
198         assert(c);
199         assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
200
201         for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) {
202
203                 if (c->weekdays_bits & (1 << x)) {
204
205                         if (l < 0) {
206                                 if (need_colon)
207                                         fputc(',', f);
208                                 else
209                                         need_colon = true;
210
211                                 fputs(days[x], f);
212                                 l = x;
213                         }
214
215                 } else if (l >= 0) {
216
217                         if (x > l + 1) {
218                                 fputc(x > l + 2 ? '-' : ',', f);
219                                 fputs(days[x-1], f);
220                         }
221
222                         l = -1;
223                 }
224         }
225
226         if (l >= 0 && x > l + 1) {
227                 fputc(x > l + 2 ? '-' : ',', f);
228                 fputs(days[x-1], f);
229         }
230 }
231
232 static void format_chain(FILE *f, int space, const CalendarComponent *c) {
233         assert(f);
234
235         if (!c) {
236                 fputc('*', f);
237                 return;
238         }
239
240         assert(c->value >= 0);
241         fprintf(f, "%0*i", space, c->value);
242
243         if (c->repeat > 0)
244                 fprintf(f, "/%i", c->repeat);
245
246         if (c->next) {
247                 fputc(',', f);
248                 format_chain(f, space, c->next);
249         }
250 }
251
252 int calendar_spec_to_string(const CalendarSpec *c, char **p) {
253         char *buf = NULL;
254         size_t sz = 0;
255         FILE *f;
256
257         assert(c);
258         assert(p);
259
260         f = open_memstream(&buf, &sz);
261         if (!f)
262                 return -ENOMEM;
263
264         if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
265                 format_weekdays(f, c);
266                 fputc(' ', f);
267         }
268
269         format_chain(f, 4, c->year);
270         fputc('-', f);
271         format_chain(f, 2, c->month);
272         fputc('-', f);
273         format_chain(f, 2, c->day);
274         fputc(' ', f);
275         format_chain(f, 2, c->hour);
276         fputc(':', f);
277         format_chain(f, 2, c->minute);
278         fputc(':', f);
279         format_chain(f, 2, c->second);
280
281         fflush(f);
282
283         if (ferror(f)) {
284                 free(buf);
285                 fclose(f);
286                 return -ENOMEM;
287         }
288
289         fclose(f);
290
291         *p = buf;
292         return 0;
293 }
294
295 static int parse_weekdays(const char **p, CalendarSpec *c) {
296         static const struct {
297                 const char *name;
298                 const int nr;
299         } day_nr[] = {
300                 { "Monday",    0 },
301                 { "Mon",       0 },
302                 { "Tuesday",   1 },
303                 { "Tue",       1 },
304                 { "Wednesday", 2 },
305                 { "Wed",       2 },
306                 { "Thursday",  3 },
307                 { "Thu",       3 },
308                 { "Friday",    4 },
309                 { "Fri",       4 },
310                 { "Saturday",  5 },
311                 { "Sat",       5 },
312                 { "Sunday",    6 },
313                 { "Sun",       6 }
314         };
315
316         int l = -1;
317         bool first = true;
318
319         assert(p);
320         assert(*p);
321         assert(c);
322
323         for (;;) {
324                 unsigned i;
325
326                 if (!first && **p == ' ')
327                         return 0;
328
329                 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
330                         size_t skip;
331
332                         if (!startswith_no_case(*p, day_nr[i].name))
333                                 continue;
334
335                         skip = strlen(day_nr[i].name);
336
337                         if ((*p)[skip] != '-' &&
338                             (*p)[skip] != ',' &&
339                             (*p)[skip] != ' ' &&
340                             (*p)[skip] != 0)
341                                 return -EINVAL;
342
343                         c->weekdays_bits |= 1 << day_nr[i].nr;
344
345                         if (l >= 0) {
346                                 int j;
347
348                                 if (l > day_nr[i].nr)
349                                         return -EINVAL;
350
351                                 for (j = l + 1; j < day_nr[i].nr; j++)
352                                         c->weekdays_bits |= 1 << j;
353                         }
354
355                         *p += skip;
356                         break;
357                 }
358
359                 /* Couldn't find this prefix, so let's assume the
360                    weekday was not specified and let's continue with
361                    the date */
362                 if (i >= ELEMENTSOF(day_nr))
363                         return first ? 0 : -EINVAL;
364
365                 /* We reached the end of the string */
366                 if (**p == 0)
367                         return 0;
368
369                 /* We reached the end of the weekday spec part */
370                 if (**p == ' ') {
371                         *p += strspn(*p, " ");
372                         return 0;
373                 }
374
375                 if (**p == '-') {
376                         if (l >= 0)
377                                 return -EINVAL;
378
379                         l = day_nr[i].nr;
380                 } else
381                         l = -1;
382
383                 *p += 1;
384                 first = false;
385         }
386 }
387
388 static int prepend_component(const char **p, CalendarComponent **c) {
389         unsigned long value, repeat = 0;
390         char *e = NULL, *ee = NULL;
391         CalendarComponent *cc;
392
393         assert(p);
394         assert(c);
395
396         errno = 0;
397         value = strtoul(*p, &e, 10);
398         if (errno > 0)
399                 return -errno;
400         if (e == *p)
401                 return -EINVAL;
402         if ((unsigned long) (int) value != value)
403                 return -ERANGE;
404
405         if (*e == '/') {
406                 repeat = strtoul(e+1, &ee, 10);
407                 if (errno > 0)
408                         return -errno;
409                 if (ee == e+1)
410                         return -EINVAL;
411                 if ((unsigned long) (int) repeat != repeat)
412                         return -ERANGE;
413                 if (repeat <= 0)
414                         return -ERANGE;
415
416                 e = ee;
417         }
418
419         if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
420                 return -EINVAL;
421
422         cc = new0(CalendarComponent, 1);
423         if (!cc)
424                 return -ENOMEM;
425
426         cc->value = value;
427         cc->repeat = repeat;
428         cc->next = *c;
429
430         *p = e;
431         *c = cc;
432
433         if (*e ==',') {
434                 *p += 1;
435                 return prepend_component(p, c);
436         }
437
438         return 0;
439 }
440
441 static int parse_chain(const char **p, CalendarComponent **c) {
442         const char *t;
443         CalendarComponent *cc = NULL;
444         int r;
445
446         assert(p);
447         assert(c);
448
449         t = *p;
450
451         if (t[0] == '*') {
452                 *p = t + 1;
453                 *c = NULL;
454                 return 0;
455         }
456
457         r = prepend_component(&t, &cc);
458         if (r < 0) {
459                 free_chain(cc);
460                 return r;
461         }
462
463         *p = t;
464         *c = cc;
465         return 0;
466 }
467
468 static int const_chain(int value, CalendarComponent **c) {
469         CalendarComponent *cc = NULL;
470
471         assert(c);
472
473         cc = new0(CalendarComponent, 1);
474         if (!cc)
475                 return -ENOMEM;
476
477         cc->value = value;
478         cc->repeat = 0;
479         cc->next = *c;
480
481         *c = cc;
482
483         return 0;
484 }
485
486 static int parse_date(const char **p, CalendarSpec *c) {
487         const char *t;
488         int r;
489         CalendarComponent *first, *second, *third;
490
491         assert(p);
492         assert(*p);
493         assert(c);
494
495         t = *p;
496
497         if (*t == 0)
498                 return 0;
499
500         r = parse_chain(&t, &first);
501         if (r < 0)
502                 return r;
503
504         /* Already the end? A ':' as separator? In that case this was a time, not a date */
505         if (*t == 0 || *t == ':') {
506                 free_chain(first);
507                 return 0;
508         }
509
510         if (*t != '-') {
511                 free_chain(first);
512                 return -EINVAL;
513         }
514
515         t++;
516         r = parse_chain(&t, &second);
517         if (r < 0) {
518                 free_chain(first);
519                 return r;
520         }
521
522         /* Got two parts, hence it's month and day */
523         if (*t == ' ' || *t == 0) {
524                 *p = t + strspn(t, " ");
525                 c->month = first;
526                 c->day = second;
527                 return 0;
528         }
529
530         if (*t != '-') {
531                 free_chain(first);
532                 free_chain(second);
533                 return -EINVAL;
534         }
535
536         t++;
537         r = parse_chain(&t, &third);
538         if (r < 0) {
539                 free_chain(first);
540                 free_chain(second);
541                 return r;
542         }
543
544         /* Got tree parts, hence it is year, month and day */
545         if (*t == ' ' || *t == 0) {
546                 *p = t + strspn(t, " ");
547                 c->year = first;
548                 c->month = second;
549                 c->day = third;
550                 return 0;
551         }
552
553         free_chain(first);
554         free_chain(second);
555         free_chain(third);
556         return -EINVAL;
557 }
558
559 static int parse_time(const char **p, CalendarSpec *c) {
560         CalendarComponent *h = NULL, *m = NULL, *s = NULL;
561         const char *t;
562         int r;
563
564         assert(p);
565         assert(*p);
566         assert(c);
567
568         t = *p;
569
570         if (*t == 0) {
571                 /* If no time is specified at all, but a date of some
572                  * kind, then this means 00:00:00 */
573                 if (c->day || c->weekdays_bits > 0)
574                         goto null_hour;
575
576                 goto finish;
577         }
578
579         r = parse_chain(&t, &h);
580         if (r < 0)
581                 goto fail;
582
583         if (*t != ':') {
584                 r = -EINVAL;
585                 goto fail;
586         }
587
588         t++;
589         r = parse_chain(&t, &m);
590         if (r < 0)
591                 goto fail;
592
593         /* Already at the end? Then it's hours and minutes, and seconds are 0 */
594         if (*t == 0) {
595                 if (m != NULL)
596                         goto null_second;
597
598                 goto finish;
599         }
600
601         if (*t != ':') {
602                 r = -EINVAL;
603                 goto fail;
604         }
605
606         t++;
607         r = parse_chain(&t, &s);
608         if (r < 0)
609                 goto fail;
610
611         /* At the end? Then it's hours, minutes and seconds */
612         if (*t == 0)
613                 goto finish;
614
615         r = -EINVAL;
616         goto fail;
617
618 null_hour:
619         r = const_chain(0, &h);
620         if (r < 0)
621                 goto fail;
622
623         r = const_chain(0, &m);
624         if (r < 0)
625                 goto fail;
626
627 null_second:
628         r = const_chain(0, &s);
629         if (r < 0)
630                 goto fail;
631
632 finish:
633         *p = t;
634         c->hour = h;
635         c->minute = m;
636         c->second = s;
637         return 0;
638
639 fail:
640         free_chain(h);
641         free_chain(m);
642         free_chain(s);
643         return r;
644 }
645
646 int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
647         CalendarSpec *c;
648         int r;
649
650         assert(p);
651         assert(spec);
652
653         if (isempty(p))
654                 return -EINVAL;
655
656         c = new0(CalendarSpec, 1);
657         if (!c)
658                 return -ENOMEM;
659
660         if (strcaseeq(p, "minutely")) {
661                 r = const_chain(0, &c->second);
662                 if (r < 0)
663                         goto fail;
664
665         } else if (strcaseeq(p, "hourly")) {
666                 r = const_chain(0, &c->minute);
667                 if (r < 0)
668                         goto fail;
669                 r = const_chain(0, &c->second);
670                 if (r < 0)
671                         goto fail;
672
673         } else if (strcaseeq(p, "daily")) {
674                 r = const_chain(0, &c->hour);
675                 if (r < 0)
676                         goto fail;
677                 r = const_chain(0, &c->minute);
678                 if (r < 0)
679                         goto fail;
680                 r = const_chain(0, &c->second);
681                 if (r < 0)
682                         goto fail;
683
684         } else if (strcaseeq(p, "monthly")) {
685                 r = const_chain(1, &c->day);
686                 if (r < 0)
687                         goto fail;
688                 r = const_chain(0, &c->hour);
689                 if (r < 0)
690                         goto fail;
691                 r = const_chain(0, &c->minute);
692                 if (r < 0)
693                         goto fail;
694                 r = const_chain(0, &c->second);
695                 if (r < 0)
696                         goto fail;
697
698         } else if (strcaseeq(p, "annually") ||
699                    strcaseeq(p, "yearly") ||
700                    strcaseeq(p, "anually") /* backwards compatibility */ ) {
701
702                 r = const_chain(1, &c->month);
703                 if (r < 0)
704                         goto fail;
705                 r = const_chain(1, &c->day);
706                 if (r < 0)
707                         goto fail;
708                 r = const_chain(0, &c->hour);
709                 if (r < 0)
710                         goto fail;
711                 r = const_chain(0, &c->minute);
712                 if (r < 0)
713                         goto fail;
714                 r = const_chain(0, &c->second);
715                 if (r < 0)
716                         goto fail;
717
718         } else if (strcaseeq(p, "weekly")) {
719
720                 c->weekdays_bits = 1;
721
722                 r = const_chain(0, &c->hour);
723                 if (r < 0)
724                         goto fail;
725                 r = const_chain(0, &c->minute);
726                 if (r < 0)
727                         goto fail;
728                 r = const_chain(0, &c->second);
729                 if (r < 0)
730                         goto fail;
731
732         } else if (strcaseeq(p, "quarterly")) {
733
734                 r = const_chain(1, &c->month);
735                 if (r < 0)
736                         goto fail;
737                 r = const_chain(4, &c->month);
738                 if (r < 0)
739                         goto fail;
740                 r = const_chain(7, &c->month);
741                 if (r < 0)
742                         goto fail;
743                 r = const_chain(10, &c->month);
744                 if (r < 0)
745                         goto fail;
746                 r = const_chain(1, &c->day);
747                 if (r < 0)
748                         goto fail;
749                 r = const_chain(0, &c->hour);
750                 if (r < 0)
751                         goto fail;
752                 r = const_chain(0, &c->minute);
753                 if (r < 0)
754                         goto fail;
755                 r = const_chain(0, &c->second);
756                 if (r < 0)
757                         goto fail;
758
759         } else if (strcaseeq(p, "biannually") ||
760                    strcaseeq(p, "bi-annually") ||
761                    strcaseeq(p, "semiannually") ||
762                    strcaseeq(p, "semi-annually")) {
763
764                 r = const_chain(1, &c->month);
765                 if (r < 0)
766                         goto fail;
767                 r = const_chain(7, &c->month);
768                 if (r < 0)
769                         goto fail;
770                 r = const_chain(1, &c->day);
771                 if (r < 0)
772                         goto fail;
773                 r = const_chain(0, &c->hour);
774                 if (r < 0)
775                         goto fail;
776                 r = const_chain(0, &c->minute);
777                 if (r < 0)
778                         goto fail;
779                 r = const_chain(0, &c->second);
780                 if (r < 0)
781                         goto fail;
782
783         } else {
784                 r = parse_weekdays(&p, c);
785                 if (r < 0)
786                         goto fail;
787
788                 r = parse_date(&p, c);
789                 if (r < 0)
790                         goto fail;
791
792                 r = parse_time(&p, c);
793                 if (r < 0)
794                         goto fail;
795
796                 if (*p != 0) {
797                         r = -EINVAL;
798                         goto fail;
799                 }
800         }
801
802         r = calendar_spec_normalize(c);
803         if (r < 0)
804                 goto fail;
805
806         if (!calendar_spec_valid(c)) {
807                 r = -EINVAL;
808                 goto fail;
809         }
810
811         *spec = c;
812         return 0;
813
814 fail:
815         calendar_spec_free(c);
816         return r;
817 }
818
819 static int find_matching_component(const CalendarComponent *c, int *val) {
820         const CalendarComponent *n;
821         int d = -1;
822         bool d_set = false;
823         int r;
824
825         assert(val);
826
827         if (!c)
828                 return 0;
829
830         while (c) {
831                 n = c->next;
832
833                 if (c->value >= *val) {
834
835                         if (!d_set || c->value < d) {
836                                 d = c->value;
837                                 d_set = true;
838                         }
839
840                 } else if (c->repeat > 0) {
841                         int k;
842
843                         k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat);
844
845                         if (!d_set || k < d) {
846                                 d = k;
847                                 d_set = true;
848                         }
849                 }
850
851                 c = n;
852         }
853
854         if (!d_set)
855                 return -ENOENT;
856
857         r = *val != d;
858         *val = d;
859         return r;
860 }
861
862 static bool tm_out_of_bounds(const struct tm *tm) {
863         struct tm t;
864         assert(tm);
865
866         t = *tm;
867
868         if (mktime(&t) == (time_t) -1)
869                 return true;
870
871         /* Did any normalization take place? If so, it was out of bounds before */
872         return
873                 t.tm_year != tm->tm_year ||
874                 t.tm_mon != tm->tm_mon ||
875                 t.tm_mday != tm->tm_mday ||
876                 t.tm_hour != tm->tm_hour ||
877                 t.tm_min != tm->tm_min ||
878                 t.tm_sec != tm->tm_sec;
879 }
880
881 static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
882         struct tm t;
883         int k;
884
885         if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
886                 return true;
887
888         t = *tm;
889         if (mktime(&t) == (time_t) -1)
890                 return false;
891
892         k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
893         return (weekdays_bits & (1 << k));
894 }
895
896 static int find_next(const CalendarSpec *spec, struct tm *tm) {
897         struct tm c;
898         int r;
899
900         assert(spec);
901         assert(tm);
902
903         c = *tm;
904
905         for (;;) {
906                 /* Normalize the current date */
907                 mktime(&c);
908                 c.tm_isdst = -1;
909
910                 c.tm_year += 1900;
911                 r = find_matching_component(spec->year, &c.tm_year);
912                 c.tm_year -= 1900;
913
914                 if (r > 0) {
915                         c.tm_mon = 0;
916                         c.tm_mday = 1;
917                         c.tm_hour = c.tm_min = c.tm_sec = 0;
918                 }
919                 if (r < 0 || tm_out_of_bounds(&c))
920                         return r;
921
922                 c.tm_mon += 1;
923                 r = find_matching_component(spec->month, &c.tm_mon);
924                 c.tm_mon -= 1;
925
926                 if (r > 0) {
927                         c.tm_mday = 1;
928                         c.tm_hour = c.tm_min = c.tm_sec = 0;
929                 }
930                 if (r < 0 || tm_out_of_bounds(&c)) {
931                         c.tm_year ++;
932                         c.tm_mon = 0;
933                         c.tm_mday = 1;
934                         c.tm_hour = c.tm_min = c.tm_sec = 0;
935                         continue;
936                 }
937
938                 r = find_matching_component(spec->day, &c.tm_mday);
939                 if (r > 0)
940                         c.tm_hour = c.tm_min = c.tm_sec = 0;
941                 if (r < 0 || tm_out_of_bounds(&c)) {
942                         c.tm_mon ++;
943                         c.tm_mday = 1;
944                         c.tm_hour = c.tm_min = c.tm_sec = 0;
945                         continue;
946                 }
947
948                 if (!matches_weekday(spec->weekdays_bits, &c)) {
949                         c.tm_mday++;
950                         c.tm_hour = c.tm_min = c.tm_sec = 0;
951                         continue;
952                 }
953
954                 r = find_matching_component(spec->hour, &c.tm_hour);
955                 if (r > 0)
956                         c.tm_min = c.tm_sec = 0;
957                 if (r < 0 || tm_out_of_bounds(&c)) {
958                         c.tm_mday ++;
959                         c.tm_hour = c.tm_min = c.tm_sec = 0;
960                         continue;
961                 }
962
963                 r = find_matching_component(spec->minute, &c.tm_min);
964                 if (r > 0)
965                         c.tm_sec = 0;
966                 if (r < 0 || tm_out_of_bounds(&c)) {
967                         c.tm_hour ++;
968                         c.tm_min = c.tm_sec = 0;
969                         continue;
970                 }
971
972                 r = find_matching_component(spec->second, &c.tm_sec);
973                 if (r < 0 || tm_out_of_bounds(&c)) {
974                         c.tm_min ++;
975                         c.tm_sec = 0;
976                         continue;
977                 }
978
979
980                 *tm = c;
981                 return 0;
982         }
983 }
984
985 int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
986         struct tm tm;
987         time_t t;
988         int r;
989
990         assert(spec);
991         assert(next);
992
993         t = (time_t) (usec / USEC_PER_SEC) + 1;
994         assert_se(localtime_r(&t, &tm));
995
996         r = find_next(spec, &tm);
997         if (r < 0)
998                 return r;
999
1000         t = mktime(&tm);
1001         if (t == (time_t) -1)
1002                 return -EINVAL;
1003
1004         *next = (usec_t) t * USEC_PER_SEC;
1005         return 0;
1006 }