]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/calendarspec.c
Merge pull request #1655 from ssahani/net
[thirdparty/systemd.git] / src / basic / 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 "alloc-util.h"
26 #include "string-util.h"
27 #include "calendarspec.h"
28 #include "fileio.h"
29
30 #define BITS_WEEKDAYS 127
31
32 static 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
42 void calendar_spec_free(CalendarSpec *c) {
43
44 if (!c)
45 return;
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
57 static 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
73 static 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
109 static 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
125 int calendar_spec_normalize(CalendarSpec *c) {
126 assert(c);
127
128 if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
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
143 _pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
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
159 _pure_ bool calendar_spec_valid(CalendarSpec *c) {
160 assert(c);
161
162 if (c->weekdays_bits > BITS_WEEKDAYS)
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
186 static 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);
202 assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
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
235 static 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
255 int calendar_spec_to_string(const CalendarSpec *c, char **p) {
256 char *buf = NULL;
257 size_t sz = 0;
258 FILE *f;
259 int r;
260
261 assert(c);
262 assert(p);
263
264 f = open_memstream(&buf, &sz);
265 if (!f)
266 return -ENOMEM;
267
268 if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
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
285 if (c->utc)
286 fputs(" UTC", f);
287
288 r = fflush_and_check(f);
289 if (r < 0) {
290 free(buf);
291 fclose(f);
292 return r;
293 }
294
295 fclose(f);
296
297 *p = buf;
298 return 0;
299 }
300
301 static 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
394 static 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);
404 if (errno > 0)
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);
413 if (errno > 0)
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
447 static 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
474 static 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;
485 cc->next = *c;
486
487 *c = cc;
488
489 return 0;
490 }
491
492 static 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
565 static int parse_calendar_time(const char **p, CalendarSpec *c) {
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
624 null_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
633 null_second:
634 r = const_chain(0, &s);
635 if (r < 0)
636 goto fail;
637
638 finish:
639 *p = t;
640 c->hour = h;
641 c->minute = m;
642 c->second = s;
643 return 0;
644
645 fail:
646 free_chain(h);
647 free_chain(m);
648 free_chain(s);
649 return r;
650 }
651
652 int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
653 CalendarSpec *c;
654 int r;
655 const char *utc;
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
667 utc = endswith_no_case(p, " UTC");
668 if (utc) {
669 c->utc = true;
670 p = strndupa(p, utc - p);
671 }
672
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")) {
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
686 } else if (strcaseeq(p, "daily")) {
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
697 } else if (strcaseeq(p, "monthly")) {
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
711 } else if (strcaseeq(p, "annually") ||
712 strcaseeq(p, "yearly") ||
713 strcaseeq(p, "anually") /* backwards compatibility */ ) {
714
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
731 } else if (strcaseeq(p, "weekly")) {
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);
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);
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
805 r = parse_calendar_time(&p, c);
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
827 fail:
828 calendar_spec_free(c);
829 return r;
830 }
831
832 static int find_matching_component(const CalendarComponent *c, int *val) {
833 const CalendarComponent *n;
834 int d = -1;
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
875 static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
876 struct tm t;
877 assert(tm);
878
879 t = *tm;
880
881 if (mktime_or_timegm(&t, utc) == (time_t) -1)
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
894 static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
895 struct tm t;
896 int k;
897
898 if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
899 return true;
900
901 t = *tm;
902 if (mktime_or_timegm(&t, utc) == (time_t) -1)
903 return false;
904
905 k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
906 return (weekdays_bits & (1 << k));
907 }
908
909 static 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 */
920 mktime_or_timegm(&c, spec->utc);
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 }
932 if (r < 0 || tm_out_of_bounds(&c, spec->utc))
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 }
943 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
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;
954 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
955 c.tm_mon ++;
956 c.tm_mday = 1;
957 c.tm_hour = c.tm_min = c.tm_sec = 0;
958 continue;
959 }
960
961 if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
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;
970 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
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;
979 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
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);
986 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
987 c.tm_min ++;
988 c.tm_sec = 0;
989 continue;
990 }
991
992
993 *tm = c;
994 return 0;
995 }
996 }
997
998 int 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;
1007 assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
1008
1009 r = find_next(spec, &tm);
1010 if (r < 0)
1011 return r;
1012
1013 t = mktime_or_timegm(&tm, spec->utc);
1014 if (t == (time_t) -1)
1015 return -EINVAL;
1016
1017 *next = (usec_t) t * USEC_PER_SEC;
1018 return 0;
1019 }