calendarspec.c revision 51ffa239e8920499a9b5f85b1d98068cf931cd14
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering/***
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering This file is part of systemd.
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering Copyright 2012 Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering systemd is free software; you can redistribute it and/or modify it
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering under the terms of the GNU Lesser General Public License as published by
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering (at your option) any later version.
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering systemd is distributed in the hope that it will be useful, but
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering Lesser General Public License for more details.
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering You should have received a copy of the GNU Lesser General Public License
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering***/
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering#include <stdlib.h>
4871690d9e32608bbd9b18505b5326c2079c9690Allin Cottrell#include <string.h>
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering#include "calendarspec.h"
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering#define BITS_WEEKDAYS 127
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poetteringstatic void free_chain(CalendarComponent *c) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek CalendarComponent *n;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering while (c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering n = c->next;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free(c);
40b71e89bae4e51768db4dc50ec64c1e9c96eec4Sebastian Thorarensen c = n;
a09abc4ae0bdc0200324eaa0416f23ff2170ec4eLennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
d18d46ecea80a7f07415edb9264af6a254fd70bbZbigniew Jędrzejewski-Szmek
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poetteringvoid calendar_spec_free(CalendarSpec *c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!c)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free_chain(c->year);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free_chain(c->month);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free_chain(c->day);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free_chain(c->hour);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free_chain(c->minute);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free_chain(c->second);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free(c);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poetteringstatic int component_compare(const void *_a, const void *_b) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering CalendarComponent * const *a = _a, * const *b = _b;
d18d46ecea80a7f07415edb9264af6a254fd70bbZbigniew Jędrzejewski-Szmek
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if ((*a)->value < (*b)->value)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return -1;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if ((*a)->value > (*b)->value)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return 1;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if ((*a)->repeat < (*b)->repeat)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return -1;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if ((*a)->repeat > (*b)->repeat)
d18d46ecea80a7f07415edb9264af6a254fd70bbZbigniew Jędrzejewski-Szmek return 1;
d18d46ecea80a7f07415edb9264af6a254fd70bbZbigniew Jędrzejewski-Szmek
d18d46ecea80a7f07415edb9264af6a254fd70bbZbigniew Jędrzejewski-Szmek return 0;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poetteringstatic void sort_chain(CalendarComponent **c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering unsigned n = 0, k;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering CalendarComponent **b, *i, **j, *next;
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek assert(c);
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek for (i = *c; i; i = i->next)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering n++;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (n <= 1)
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering return;
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering j = b = alloca(sizeof(CalendarComponent*) * n);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering for (i = *c; i; i = i->next)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering *(j++) = i;
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering qsort(b, n, sizeof(CalendarComponent*), component_compare);
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering b[n-1]->next = NULL;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering next = b[n-1];
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering /* Drop non-unique entries */
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering for (k = n-1; k > 0; k--) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (b[k-1]->value == next->value &&
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering b[k-1]->repeat == next->repeat) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free(b[k-1]);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering continue;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering b[k-1]->next = next;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering next = b[k-1];
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering *c = next;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poetteringstatic void fix_year(CalendarComponent *c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering /* Turns 12 → 2012, 89 → 1989 */
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering while(c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering CalendarComponent *n = c->next;
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek if (c->value >= 0 && c->value < 70)
8c0b803b97bb0ee6603d9be85fb6b69cd6081eafLennart Poettering c->value += 2000;
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek if (c->value >= 70 && c->value < 100)
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek c->value += 1900;
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering c = n;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek}
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poetteringint calendar_spec_normalize(CalendarSpec *c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering assert(c);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering c->weekdays_bits = -1;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fix_year(c->year);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering sort_chain(&c->year);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering sort_chain(&c->month);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering sort_chain(&c->day);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering sort_chain(&c->hour);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering sort_chain(&c->minute);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering sort_chain(&c->second);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
12a717f8347f3daf0ae46a2b71c7d011d9c12feaZbigniew Jędrzejewski-Szmek return 0;
92ee6447b1deef7c79962a8121fdf8e58acb3a83Zbigniew Jędrzejewski-Szmek}
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!c)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return true;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->value < from || c->value > to)
d18d46ecea80a7f07415edb9264af6a254fd70bbZbigniew Jędrzejewski-Szmek return false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->value + c->repeat > to)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->next)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return chain_valid(c->next, from, to);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return true;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek_pure_ bool calendar_spec_valid(CalendarSpec *c) {
a174f94d529c7ae9be589867308b669ec9b4dcc0Lennart Poettering assert(c);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->weekdays_bits > BITS_WEEKDAYS)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!chain_valid(c->year, 1970, 2199))
2a0e0692565f0435657c93498e09cbb2d3517152Shawn Landden return false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!chain_valid(c->month, 1, 12))
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
2a0e0692565f0435657c93498e09cbb2d3517152Shawn Landden if (!chain_valid(c->day, 1, 31))
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!chain_valid(c->hour, 0, 23))
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return false;
2a0e0692565f0435657c93498e09cbb2d3517152Shawn Landden
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!chain_valid(c->minute, 0, 59))
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!chain_valid(c->second, 0, 59))
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return false;
2a0e0692565f0435657c93498e09cbb2d3517152Shawn Landden
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return true;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poetteringstatic void format_weekdays(FILE *f, const CalendarSpec *c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering static const char *const days[] = {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering "Mon",
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering "Tue",
1f6b411372076426c0faf0bb350437fb4d82931fLennart Poettering "Wed",
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering "Thu",
2a0e0692565f0435657c93498e09cbb2d3517152Shawn Landden "Fri",
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering "Sat",
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering "Sun"
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering };
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering int l, x;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering bool need_colon = false;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
1f6b411372076426c0faf0bb350437fb4d82931fLennart Poettering assert(f);
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek assert(c);
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
2a0e0692565f0435657c93498e09cbb2d3517152Shawn Landden
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) {
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek if (c->weekdays_bits & (1 << x)) {
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek if (l < 0) {
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek if (need_colon)
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek fputc(',', f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering else
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering need_colon = true;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputs(days[x], f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering l = x;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering } else if (l >= 0) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (x > l + 1) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputc(x > l + 2 ? '-' : ',', f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputs(days[x-1], f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering l = -1;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (l >= 0 && x > l + 1) {
505b6a61c22d5565e9308045c7b9bf79f7d0517eLennart Poettering fputc(x > l + 2 ? '-' : ',', f);
fa1c4b518ec7d8ec2d647213ee651cde4d6c9d7eZbigniew Jędrzejewski-Szmek fputs(days[x-1], f);
505b6a61c22d5565e9308045c7b9bf79f7d0517eLennart Poettering }
505b6a61c22d5565e9308045c7b9bf79f7d0517eLennart Poettering}
505b6a61c22d5565e9308045c7b9bf79f7d0517eLennart Poettering
505b6a61c22d5565e9308045c7b9bf79f7d0517eLennart Poetteringstatic void format_chain(FILE *f, int space, const CalendarComponent *c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering assert(f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputc('*', f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering assert(c->value >= 0);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fprintf(f, "%0*i", space, c->value);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->repeat > 0)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fprintf(f, "/%i", c->repeat);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->next) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputc(',', f);
d18d46ecea80a7f07415edb9264af6a254fd70bbZbigniew Jędrzejewski-Szmek format_chain(f, space, c->next);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek
a174f94d529c7ae9be589867308b669ec9b4dcc0Lennart Poetteringint calendar_spec_to_string(const CalendarSpec *c, char **p) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering char *buf = NULL;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering size_t sz = 0;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering FILE *f;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering int r;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering assert(c);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering assert(p);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering f = open_memstream(&buf, &sz);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (!f)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return -ENOMEM;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek format_weekdays(f, c);
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek fputc(' ', f);
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek }
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek format_chain(f, 4, c->year);
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek fputc('-', f);
874bc134ac6504c45e94174e37af13ff21a6bfe2Zbigniew Jędrzejewski-Szmek format_chain(f, 2, c->month);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputc('-', f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering format_chain(f, 2, c->day);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputc(' ', f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering format_chain(f, 2, c->hour);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputc(':', f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering format_chain(f, 2, c->minute);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fputc(':', f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering format_chain(f, 2, c->second);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (c->utc)
40b71e89bae4e51768db4dc50ec64c1e9c96eec4Sebastian Thorarensen fputs(" UTC", f);
40b71e89bae4e51768db4dc50ec64c1e9c96eec4Sebastian Thorarensen
40b71e89bae4e51768db4dc50ec64c1e9c96eec4Sebastian Thorarensen r = fflush_and_check(f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (r < 0) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free(buf);
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek fclose(f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return r;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering fclose(f);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering *p = buf;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return 0;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poetteringstatic int parse_weekdays(const char **p, CalendarSpec *c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering static const struct {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering const char *name;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering const int nr;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering } day_nr[] = {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Monday", 0 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Mon", 0 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Tuesday", 1 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Tue", 1 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Wednesday", 2 },
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering { "Wed", 2 },
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering { "Thursday", 3 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Thu", 3 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Friday", 4 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Fri", 4 },
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering { "Saturday", 5 },
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering { "Sat", 5 },
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering { "Sunday", 6 },
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering { "Sun", 6 }
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering };
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering int l = -1;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering bool first = true;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering assert(p);
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering assert(*p);
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering assert(c);
73843b52585d42cc1a970a1c664818ece6942e9eLennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering for (;;) {
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering unsigned i;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering if (!first && **p == ' ')
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering return 0;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering for (i = 0; i < ELEMENTSOF(day_nr); i++) {
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering size_t skip;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering if (!startswith_no_case(*p, day_nr[i].name))
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering continue;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering skip = strlen(day_nr[i].name);
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering if ((*p)[skip] != '-' &&
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt (*p)[skip] != ',' &&
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering (*p)[skip] != ' ' &&
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering (*p)[skip] != 0)
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering return -EINVAL;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering c->weekdays_bits |= 1 << day_nr[i].nr;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering if (l >= 0) {
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering int j;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering if (l > day_nr[i].nr)
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering return -EINVAL;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering for (j = l + 1; j < day_nr[i].nr; j++)
ae6c3cc009a21df4b51851fb8fe3fde0b7d6d8f0Lennart Poettering c->weekdays_bits |= 1 << j;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering }
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering *p += skip;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering break;
1dfa7e79a60de680086b1d93fcc3629b463f58bdLennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt /* Couldn't find this prefix, so let's assume the
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering weekday was not specified and let's continue with
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering the date */
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (i >= ELEMENTSOF(day_nr))
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return first ? 0 : -EINVAL;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering /* We reached the end of the string */
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (**p == 0)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return 0;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering /* We reached the end of the weekday spec part */
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (**p == ' ') {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering *p += strspn(*p, " ");
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return 0;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (**p == '-') {
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering if (l >= 0)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return -EINVAL;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering l = day_nr[i].nr;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering } else
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering l = -1;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering *p += 1;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering first = false;
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt }
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering}
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poetteringstatic int prepend_component(const char **p, CalendarComponent **c) {
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering unsigned long value, repeat = 0;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering char *e = NULL, *ee = NULL;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering CalendarComponent *cc;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering assert(p);
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering assert(c);
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering errno = 0;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering value = strtoul(*p, &e, 10);
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering if (errno > 0)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return -errno;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering if (e == *p)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return -EINVAL;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering if ((unsigned long) (int) value != value)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return -ERANGE;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering if (*e == '/') {
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering repeat = strtoul(e+1, &ee, 10);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt if (errno > 0)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return -errno;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering if (ee == e+1)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return -EINVAL;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if ((unsigned long) (int) repeat != repeat)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return -ERANGE;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (repeat <= 0)
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering return -ERANGE;
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering e = ee;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
f7a5bb2842037fa27dbc99d92c3fee7fe1bbbc2aZbigniew Jędrzejewski-Szmek return -EINVAL;
f7a5bb2842037fa27dbc99d92c3fee7fe1bbbc2aZbigniew Jędrzejewski-Szmek
f7a5bb2842037fa27dbc99d92c3fee7fe1bbbc2aZbigniew Jędrzejewski-Szmek cc = new0(CalendarComponent, 1);
f7a5bb2842037fa27dbc99d92c3fee7fe1bbbc2aZbigniew Jędrzejewski-Szmek if (!cc)
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return -ENOMEM;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt cc->value = value;
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt cc->repeat = repeat;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering cc->next = *c;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering *p = e;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering *c = cc;
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt if (*e ==',') {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering *p += 1;
4a61c3e51e96a747c30598d78ee3a24e7c569e9fZbigniew Jędrzejewski-Szmek return prepend_component(p, c);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering }
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return 0;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering}
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidtstatic int parse_chain(const char **p, CalendarComponent **c) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering const char *t;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering CalendarComponent *cc = NULL;
6baa7db00812437bbc87e73faa1a11b6cf631958Lennart Poettering int r;
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poettering
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poettering assert(p);
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt assert(c);
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering t = *p;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (t[0] == '*') {
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt *p = t + 1;
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt *c = NULL;
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return 0;
8531ae707d4d0203e83304d4af948b8169a5fce1Lennart Poettering }
23bbb0de4e3f85d9704a5c12a5afa2dfa0159e41Michal Schmidt
23bbb0de4e3f85d9704a5c12a5afa2dfa0159e41Michal Schmidt r = prepend_component(&t, &cc);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering if (r < 0) {
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering free_chain(cc);
0153028ae379eb7c9a463c548ef73ea392c6cdb0Lennart Poettering return r;
}
*p = t;
*c = cc;
return 0;
}
static int const_chain(int value, CalendarComponent **c) {
CalendarComponent *cc = NULL;
assert(c);
cc = new0(CalendarComponent, 1);
if (!cc)
return -ENOMEM;
cc->value = value;
cc->repeat = 0;
cc->next = *c;
*c = cc;
return 0;
}
static int parse_date(const char **p, CalendarSpec *c) {
const char *t;
int r;
CalendarComponent *first, *second, *third;
assert(p);
assert(*p);
assert(c);
t = *p;
if (*t == 0)
return 0;
r = parse_chain(&t, &first);
if (r < 0)
return r;
/* Already the end? A ':' as separator? In that case this was a time, not a date */
if (*t == 0 || *t == ':') {
free_chain(first);
return 0;
}
if (*t != '-') {
free_chain(first);
return -EINVAL;
}
t++;
r = parse_chain(&t, &second);
if (r < 0) {
free_chain(first);
return r;
}
/* Got two parts, hence it's month and day */
if (*t == ' ' || *t == 0) {
*p = t + strspn(t, " ");
c->month = first;
c->day = second;
return 0;
}
if (*t != '-') {
free_chain(first);
free_chain(second);
return -EINVAL;
}
t++;
r = parse_chain(&t, &third);
if (r < 0) {
free_chain(first);
free_chain(second);
return r;
}
/* Got tree parts, hence it is year, month and day */
if (*t == ' ' || *t == 0) {
*p = t + strspn(t, " ");
c->year = first;
c->month = second;
c->day = third;
return 0;
}
free_chain(first);
free_chain(second);
free_chain(third);
return -EINVAL;
}
static int parse_time(const char **p, CalendarSpec *c) {
CalendarComponent *h = NULL, *m = NULL, *s = NULL;
const char *t;
int r;
assert(p);
assert(*p);
assert(c);
t = *p;
if (*t == 0) {
/* If no time is specified at all, but a date of some
* kind, then this means 00:00:00 */
if (c->day || c->weekdays_bits > 0)
goto null_hour;
goto finish;
}
r = parse_chain(&t, &h);
if (r < 0)
goto fail;
if (*t != ':') {
r = -EINVAL;
goto fail;
}
t++;
r = parse_chain(&t, &m);
if (r < 0)
goto fail;
/* Already at the end? Then it's hours and minutes, and seconds are 0 */
if (*t == 0) {
if (m != NULL)
goto null_second;
goto finish;
}
if (*t != ':') {
r = -EINVAL;
goto fail;
}
t++;
r = parse_chain(&t, &s);
if (r < 0)
goto fail;
/* At the end? Then it's hours, minutes and seconds */
if (*t == 0)
goto finish;
r = -EINVAL;
goto fail;
null_hour:
r = const_chain(0, &h);
if (r < 0)
goto fail;
r = const_chain(0, &m);
if (r < 0)
goto fail;
null_second:
r = const_chain(0, &s);
if (r < 0)
goto fail;
finish:
*p = t;
c->hour = h;
c->minute = m;
c->second = s;
return 0;
fail:
free_chain(h);
free_chain(m);
free_chain(s);
return r;
}
int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
CalendarSpec *c;
int r;
assert(p);
assert(spec);
if (isempty(p))
return -EINVAL;
c = new0(CalendarSpec, 1);
if (!c)
return -ENOMEM;
c->utc = endswith_no_case(p, "UTC");
if (c->utc)
p = strndupa(p, strlen(p) - strlen(" UTC"));
if (strcaseeq(p, "minutely")) {
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else if (strcaseeq(p, "hourly")) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else if (strcaseeq(p, "daily")) {
r = const_chain(0, &c->hour);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else if (strcaseeq(p, "monthly")) {
r = const_chain(1, &c->day);
if (r < 0)
goto fail;
r = const_chain(0, &c->hour);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else if (strcaseeq(p, "annually") ||
strcaseeq(p, "yearly") ||
strcaseeq(p, "anually") /* backwards compatibility */ ) {
r = const_chain(1, &c->month);
if (r < 0)
goto fail;
r = const_chain(1, &c->day);
if (r < 0)
goto fail;
r = const_chain(0, &c->hour);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else if (strcaseeq(p, "weekly")) {
c->weekdays_bits = 1;
r = const_chain(0, &c->hour);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else if (strcaseeq(p, "quarterly")) {
r = const_chain(1, &c->month);
if (r < 0)
goto fail;
r = const_chain(4, &c->month);
if (r < 0)
goto fail;
r = const_chain(7, &c->month);
if (r < 0)
goto fail;
r = const_chain(10, &c->month);
if (r < 0)
goto fail;
r = const_chain(1, &c->day);
if (r < 0)
goto fail;
r = const_chain(0, &c->hour);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else if (strcaseeq(p, "biannually") ||
strcaseeq(p, "bi-annually") ||
strcaseeq(p, "semiannually") ||
strcaseeq(p, "semi-annually")) {
r = const_chain(1, &c->month);
if (r < 0)
goto fail;
r = const_chain(7, &c->month);
if (r < 0)
goto fail;
r = const_chain(1, &c->day);
if (r < 0)
goto fail;
r = const_chain(0, &c->hour);
if (r < 0)
goto fail;
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
r = const_chain(0, &c->second);
if (r < 0)
goto fail;
} else {
r = parse_weekdays(&p, c);
if (r < 0)
goto fail;
r = parse_date(&p, c);
if (r < 0)
goto fail;
r = parse_time(&p, c);
if (r < 0)
goto fail;
if (*p != 0) {
r = -EINVAL;
goto fail;
}
}
r = calendar_spec_normalize(c);
if (r < 0)
goto fail;
if (!calendar_spec_valid(c)) {
r = -EINVAL;
goto fail;
}
*spec = c;
return 0;
fail:
calendar_spec_free(c);
return r;
}
static int find_matching_component(const CalendarComponent *c, int *val) {
const CalendarComponent *n;
int d = -1;
bool d_set = false;
int r;
assert(val);
if (!c)
return 0;
while (c) {
n = c->next;
if (c->value >= *val) {
if (!d_set || c->value < d) {
d = c->value;
d_set = true;
}
} else if (c->repeat > 0) {
int k;
k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat);
if (!d_set || k < d) {
d = k;
d_set = true;
}
}
c = n;
}
if (!d_set)
return -ENOENT;
r = *val != d;
*val = d;
return r;
}
static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
struct tm t;
assert(tm);
t = *tm;
if (mktime_or_timegm(&t, utc) == (time_t) -1)
return true;
/* Did any normalization take place? If so, it was out of bounds before */
return
t.tm_year != tm->tm_year ||
t.tm_mon != tm->tm_mon ||
t.tm_mday != tm->tm_mday ||
t.tm_hour != tm->tm_hour ||
t.tm_min != tm->tm_min ||
t.tm_sec != tm->tm_sec;
}
static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
struct tm t;
int k;
if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
return true;
t = *tm;
if (mktime_or_timegm(&t, utc) == (time_t) -1)
return false;
k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
return (weekdays_bits & (1 << k));
}
static int find_next(const CalendarSpec *spec, struct tm *tm) {
struct tm c;
int r;
assert(spec);
assert(tm);
c = *tm;
for (;;) {
/* Normalize the current date */
mktime_or_timegm(&c, spec->utc);
c.tm_isdst = -1;
c.tm_year += 1900;
r = find_matching_component(spec->year, &c.tm_year);
c.tm_year -= 1900;
if (r > 0) {
c.tm_mon = 0;
c.tm_mday = 1;
c.tm_hour = c.tm_min = c.tm_sec = 0;
}
if (r < 0 || tm_out_of_bounds(&c, spec->utc))
return r;
c.tm_mon += 1;
r = find_matching_component(spec->month, &c.tm_mon);
c.tm_mon -= 1;
if (r > 0) {
c.tm_mday = 1;
c.tm_hour = c.tm_min = c.tm_sec = 0;
}
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_year ++;
c.tm_mon = 0;
c.tm_mday = 1;
c.tm_hour = c.tm_min = c.tm_sec = 0;
continue;
}
r = find_matching_component(spec->day, &c.tm_mday);
if (r > 0)
c.tm_hour = c.tm_min = c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mon ++;
c.tm_mday = 1;
c.tm_hour = c.tm_min = c.tm_sec = 0;
continue;
}
if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
c.tm_mday++;
c.tm_hour = c.tm_min = c.tm_sec = 0;
continue;
}
r = find_matching_component(spec->hour, &c.tm_hour);
if (r > 0)
c.tm_min = c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mday ++;
c.tm_hour = c.tm_min = c.tm_sec = 0;
continue;
}
r = find_matching_component(spec->minute, &c.tm_min);
if (r > 0)
c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_hour ++;
c.tm_min = c.tm_sec = 0;
continue;
}
r = find_matching_component(spec->second, &c.tm_sec);
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_min ++;
c.tm_sec = 0;
continue;
}
*tm = c;
return 0;
}
}
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
struct tm tm;
time_t t;
int r;
assert(spec);
assert(next);
t = (time_t) (usec / USEC_PER_SEC) + 1;
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
r = find_next(spec, &tm);
if (r < 0)
return r;
t = mktime_or_timegm(&tm, spec->utc);
if (t == (time_t) -1)
return -EINVAL;
*next = (usec_t) t * USEC_PER_SEC;
return 0;
}