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