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