]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/calendarspec.c
util-lib: split our string related calls from util.[ch] into its own file string...
[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
07630cea 25#include "string-util.h"
36697dc0
LP
26#include "calendarspec.h"
27
5b99bc57 28#define BITS_WEEKDAYS 127
489464d0 29
36697dc0
LP
30static 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
40void calendar_spec_free(CalendarSpec *c) {
0b76b4d8
LP
41
42 if (!c)
43 return;
36697dc0
LP
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
55static 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
71static 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
107static 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
123int calendar_spec_normalize(CalendarSpec *c) {
124 assert(c);
125
489464d0 126 if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
36697dc0
LP
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
44a6b1b6 141_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
36697dc0
LP
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
44a6b1b6 157_pure_ bool calendar_spec_valid(CalendarSpec *c) {
36697dc0
LP
158 assert(c);
159
489464d0 160 if (c->weekdays_bits > BITS_WEEKDAYS)
36697dc0
LP
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
184static 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);
489464d0 200 assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
36697dc0
LP
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
233static 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
253int calendar_spec_to_string(const CalendarSpec *c, char **p) {
254 char *buf = NULL;
255 size_t sz = 0;
256 FILE *f;
dacd6cee 257 int r;
36697dc0
LP
258
259 assert(c);
260 assert(p);
261
262 f = open_memstream(&buf, &sz);
263 if (!f)
264 return -ENOMEM;
265
489464d0 266 if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
36697dc0
LP
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
51ffa239
HV
283 if (c->utc)
284 fputs(" UTC", f);
285
dacd6cee
LP
286 r = fflush_and_check(f);
287 if (r < 0) {
36697dc0
LP
288 free(buf);
289 fclose(f);
dacd6cee 290 return r;
36697dc0
LP
291 }
292
293 fclose(f);
294
295 *p = buf;
296 return 0;
297}
298
299static 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
392static 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);
8333c77e 402 if (errno > 0)
36697dc0
LP
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);
8333c77e 411 if (errno > 0)
36697dc0
LP
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
445static 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
472static 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;
dbfd41e2 483 cc->next = *c;
36697dc0
LP
484
485 *c = cc;
486
487 return 0;
488}
489
490static 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
563static 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
622null_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
631null_second:
632 r = const_chain(0, &s);
633 if (r < 0)
634 goto fail;
635
636finish:
637 *p = t;
638 c->hour = h;
639 c->minute = m;
640 c->second = s;
641 return 0;
642
643fail:
644 free_chain(h);
645 free_chain(m);
646 free_chain(s);
647 return r;
648}
649
650int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
651 CalendarSpec *c;
652 int r;
653
654 assert(p);
655 assert(spec);
656
657 if (isempty(p))
658 return -EINVAL;
659
660 c = new0(CalendarSpec, 1);
661 if (!c)
662 return -ENOMEM;
663
51ffa239
HV
664 c->utc = endswith_no_case(p, "UTC");
665 if (c->utc)
666 p = strndupa(p, strlen(p) - strlen(" UTC"));
667
272ac205
DM
668 if (strcaseeq(p, "minutely")) {
669 r = const_chain(0, &c->second);
670 if (r < 0)
671 goto fail;
672
673 } else if (strcaseeq(p, "hourly")) {
36697dc0
LP
674 r = const_chain(0, &c->minute);
675 if (r < 0)
676 goto fail;
677 r = const_chain(0, &c->second);
678 if (r < 0)
679 goto fail;
680
b43d1d01 681 } else if (strcaseeq(p, "daily")) {
36697dc0
LP
682 r = const_chain(0, &c->hour);
683 if (r < 0)
684 goto fail;
685 r = const_chain(0, &c->minute);
686 if (r < 0)
687 goto fail;
688 r = const_chain(0, &c->second);
689 if (r < 0)
690 goto fail;
691
b43d1d01 692 } else if (strcaseeq(p, "monthly")) {
36697dc0
LP
693 r = const_chain(1, &c->day);
694 if (r < 0)
695 goto fail;
696 r = const_chain(0, &c->hour);
697 if (r < 0)
698 goto fail;
699 r = const_chain(0, &c->minute);
700 if (r < 0)
701 goto fail;
702 r = const_chain(0, &c->second);
703 if (r < 0)
704 goto fail;
705
dbfd41e2
LP
706 } else if (strcaseeq(p, "annually") ||
707 strcaseeq(p, "yearly") ||
708 strcaseeq(p, "anually") /* backwards compatibility */ ) {
709
13516818
LP
710 r = const_chain(1, &c->month);
711 if (r < 0)
712 goto fail;
713 r = const_chain(1, &c->day);
714 if (r < 0)
715 goto fail;
716 r = const_chain(0, &c->hour);
717 if (r < 0)
718 goto fail;
719 r = const_chain(0, &c->minute);
720 if (r < 0)
721 goto fail;
722 r = const_chain(0, &c->second);
723 if (r < 0)
724 goto fail;
725
b43d1d01 726 } else if (strcaseeq(p, "weekly")) {
36697dc0
LP
727
728 c->weekdays_bits = 1;
729
730 r = const_chain(0, &c->hour);
731 if (r < 0)
732 goto fail;
733 r = const_chain(0, &c->minute);
734 if (r < 0)
735 goto fail;
736 r = const_chain(0, &c->second);
dbfd41e2
LP
737 if (r < 0)
738 goto fail;
739
740 } else if (strcaseeq(p, "quarterly")) {
741
742 r = const_chain(1, &c->month);
743 if (r < 0)
744 goto fail;
745 r = const_chain(4, &c->month);
746 if (r < 0)
747 goto fail;
748 r = const_chain(7, &c->month);
749 if (r < 0)
750 goto fail;
751 r = const_chain(10, &c->month);
752 if (r < 0)
753 goto fail;
754 r = const_chain(1, &c->day);
755 if (r < 0)
756 goto fail;
757 r = const_chain(0, &c->hour);
758 if (r < 0)
759 goto fail;
760 r = const_chain(0, &c->minute);
761 if (r < 0)
762 goto fail;
763 r = const_chain(0, &c->second);
764 if (r < 0)
765 goto fail;
766
767 } else if (strcaseeq(p, "biannually") ||
768 strcaseeq(p, "bi-annually") ||
769 strcaseeq(p, "semiannually") ||
770 strcaseeq(p, "semi-annually")) {
771
772 r = const_chain(1, &c->month);
773 if (r < 0)
774 goto fail;
775 r = const_chain(7, &c->month);
776 if (r < 0)
777 goto fail;
778 r = const_chain(1, &c->day);
779 if (r < 0)
780 goto fail;
781 r = const_chain(0, &c->hour);
782 if (r < 0)
783 goto fail;
784 r = const_chain(0, &c->minute);
785 if (r < 0)
786 goto fail;
787 r = const_chain(0, &c->second);
36697dc0
LP
788 if (r < 0)
789 goto fail;
790
791 } else {
792 r = parse_weekdays(&p, c);
793 if (r < 0)
794 goto fail;
795
796 r = parse_date(&p, c);
797 if (r < 0)
798 goto fail;
799
800 r = parse_time(&p, c);
801 if (r < 0)
802 goto fail;
803
804 if (*p != 0) {
805 r = -EINVAL;
806 goto fail;
807 }
808 }
809
810 r = calendar_spec_normalize(c);
811 if (r < 0)
812 goto fail;
813
814 if (!calendar_spec_valid(c)) {
815 r = -EINVAL;
816 goto fail;
817 }
818
819 *spec = c;
820 return 0;
821
822fail:
823 calendar_spec_free(c);
824 return r;
825}
826
827static int find_matching_component(const CalendarComponent *c, int *val) {
828 const CalendarComponent *n;
660ddc72 829 int d = -1;
36697dc0
LP
830 bool d_set = false;
831 int r;
832
833 assert(val);
834
835 if (!c)
836 return 0;
837
838 while (c) {
839 n = c->next;
840
841 if (c->value >= *val) {
842
843 if (!d_set || c->value < d) {
844 d = c->value;
845 d_set = true;
846 }
847
848 } else if (c->repeat > 0) {
849 int k;
850
851 k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat);
852
853 if (!d_set || k < d) {
854 d = k;
855 d_set = true;
856 }
857 }
858
859 c = n;
860 }
861
862 if (!d_set)
863 return -ENOENT;
864
865 r = *val != d;
866 *val = d;
867 return r;
868}
869
51ffa239 870static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
36697dc0
LP
871 struct tm t;
872 assert(tm);
873
874 t = *tm;
875
51ffa239 876 if (mktime_or_timegm(&t, utc) == (time_t) -1)
36697dc0
LP
877 return true;
878
879 /* Did any normalization take place? If so, it was out of bounds before */
880 return
881 t.tm_year != tm->tm_year ||
882 t.tm_mon != tm->tm_mon ||
883 t.tm_mday != tm->tm_mday ||
884 t.tm_hour != tm->tm_hour ||
885 t.tm_min != tm->tm_min ||
886 t.tm_sec != tm->tm_sec;
887}
888
51ffa239 889static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
36697dc0
LP
890 struct tm t;
891 int k;
892
489464d0 893 if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
36697dc0
LP
894 return true;
895
896 t = *tm;
51ffa239 897 if (mktime_or_timegm(&t, utc) == (time_t) -1)
36697dc0
LP
898 return false;
899
900 k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
901 return (weekdays_bits & (1 << k));
902}
903
904static int find_next(const CalendarSpec *spec, struct tm *tm) {
905 struct tm c;
906 int r;
907
908 assert(spec);
909 assert(tm);
910
911 c = *tm;
912
913 for (;;) {
914 /* Normalize the current date */
51ffa239 915 mktime_or_timegm(&c, spec->utc);
36697dc0
LP
916 c.tm_isdst = -1;
917
918 c.tm_year += 1900;
919 r = find_matching_component(spec->year, &c.tm_year);
920 c.tm_year -= 1900;
921
922 if (r > 0) {
923 c.tm_mon = 0;
924 c.tm_mday = 1;
925 c.tm_hour = c.tm_min = c.tm_sec = 0;
926 }
51ffa239 927 if (r < 0 || tm_out_of_bounds(&c, spec->utc))
36697dc0
LP
928 return r;
929
930 c.tm_mon += 1;
931 r = find_matching_component(spec->month, &c.tm_mon);
932 c.tm_mon -= 1;
933
934 if (r > 0) {
935 c.tm_mday = 1;
936 c.tm_hour = c.tm_min = c.tm_sec = 0;
937 }
51ffa239 938 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
36697dc0
LP
939 c.tm_year ++;
940 c.tm_mon = 0;
941 c.tm_mday = 1;
942 c.tm_hour = c.tm_min = c.tm_sec = 0;
943 continue;
944 }
945
946 r = find_matching_component(spec->day, &c.tm_mday);
947 if (r > 0)
948 c.tm_hour = c.tm_min = c.tm_sec = 0;
51ffa239 949 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
36697dc0
LP
950 c.tm_mon ++;
951 c.tm_mday = 1;
952 c.tm_hour = c.tm_min = c.tm_sec = 0;
953 continue;
954 }
955
51ffa239 956 if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
36697dc0
LP
957 c.tm_mday++;
958 c.tm_hour = c.tm_min = c.tm_sec = 0;
959 continue;
960 }
961
962 r = find_matching_component(spec->hour, &c.tm_hour);
963 if (r > 0)
964 c.tm_min = c.tm_sec = 0;
51ffa239 965 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
36697dc0
LP
966 c.tm_mday ++;
967 c.tm_hour = c.tm_min = c.tm_sec = 0;
968 continue;
969 }
970
971 r = find_matching_component(spec->minute, &c.tm_min);
972 if (r > 0)
973 c.tm_sec = 0;
51ffa239 974 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
36697dc0
LP
975 c.tm_hour ++;
976 c.tm_min = c.tm_sec = 0;
977 continue;
978 }
979
980 r = find_matching_component(spec->second, &c.tm_sec);
51ffa239 981 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
36697dc0
LP
982 c.tm_min ++;
983 c.tm_sec = 0;
984 continue;
985 }
986
987
988 *tm = c;
989 return 0;
990 }
991}
992
993int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
994 struct tm tm;
995 time_t t;
996 int r;
997
998 assert(spec);
999 assert(next);
1000
1001 t = (time_t) (usec / USEC_PER_SEC) + 1;
51ffa239 1002 assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
36697dc0
LP
1003
1004 r = find_next(spec, &tm);
1005 if (r < 0)
1006 return r;
1007
51ffa239 1008 t = mktime_or_timegm(&tm, spec->utc);
36697dc0
LP
1009 if (t == (time_t) -1)
1010 return -EINVAL;
1011
36697dc0
LP
1012 *next = (usec_t) t * USEC_PER_SEC;
1013 return 0;
1014}