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