1/*
2 * $Id: duration.c 4518 2011-02-24 15:39:09Z matthijs $
3 *
4 * Copyright (c) 2009 NLNet Labs. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29/**
30 *
31 * This file is copied from the OpenDNSSEC source repository
32 * and only slightly adapted to make it fit.
33 */
34
35/**
36 *
37 * Durations.
38 */
39
40#include <ldns/config.h>
41#include <ldns/duration.h>
42
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <time.h>
47
48
49/**
50 * Create a new 'instant' duration.
51 *
52 */
53ldns_duration_type*
54ldns_duration_create(void)
55{
56    ldns_duration_type* duration;
57
58    duration = malloc(sizeof(ldns_duration_type));
59    if (!duration) {
60        return NULL;
61    }
62    duration->years = 0;
63    duration->months = 0;
64    duration->weeks = 0;
65    duration->days = 0;
66    duration->hours = 0;
67    duration->minutes = 0;
68    duration->seconds = 0;
69    return duration;
70}
71
72
73/**
74 * Compare durations.
75 *
76 */
77int
78ldns_duration_compare(const ldns_duration_type* d1, const ldns_duration_type* d2)
79{
80    if (!d1 && !d2) {
81        return 0;
82    }
83    if (!d1 || !d2) {
84        return d1?-1:1;
85    }
86
87    if (d1->years != d2->years) {
88        return (int) (d1->years - d2->years);
89    }
90    if (d1->months != d2->months) {
91        return (int) (d1->months - d2->months);
92    }
93    if (d1->weeks != d2->weeks) {
94        return (int) (d1->weeks - d2->weeks);
95    }
96    if (d1->days != d2->days) {
97        return (int) (d1->days - d2->days);
98    }
99    if (d1->hours != d2->hours) {
100        return (int) (d1->hours - d2->hours);
101    }
102    if (d1->minutes != d2->minutes) {
103        return (int) (d1->minutes - d2->minutes);
104    }
105    if (d1->seconds != d2->seconds) {
106        return (int) (d1->seconds - d2->seconds);
107    }
108
109    return 0;
110}
111
112
113/**
114 * Create a duration from string.
115 *
116 */
117ldns_duration_type*
118ldns_duration_create_from_string(const char* str)
119{
120    ldns_duration_type* duration = ldns_duration_create();
121    char* P, *X, *T, *W;
122    int not_weeks = 0;
123
124    if (!duration) {
125        return NULL;
126    }
127    if (!str) {
128        return duration;
129    }
130
131    P = strchr(str, 'P');
132    if (!P) {
133	ldns_duration_cleanup(duration);
134        return NULL;
135    }
136
137    T = strchr(str, 'T');
138    X = strchr(str, 'Y');
139    if (X) {
140        duration->years = (time_t) atoi(str+1);
141        str = X;
142        not_weeks = 1;
143    }
144    X = strchr(str, 'M');
145    if (X && (!T || (size_t) (X-P) < (size_t) (T-P))) {
146        duration->months = (time_t) atoi(str+1);
147        str = X;
148        not_weeks = 1;
149    }
150    X = strchr(str, 'D');
151    if (X) {
152        duration->days = (time_t) atoi(str+1);
153        str = X;
154        not_weeks = 1;
155    }
156    if (T) {
157        str = T;
158        not_weeks = 1;
159    }
160    X = strchr(str, 'H');
161    if (X && T) {
162        duration->hours = (time_t) atoi(str+1);
163        str = X;
164        not_weeks = 1;
165    }
166    X = strrchr(str, 'M');
167    if (X && T && (size_t) (X-P) > (size_t) (T-P)) {
168        duration->minutes = (time_t) atoi(str+1);
169        str = X;
170        not_weeks = 1;
171    }
172    X = strchr(str, 'S');
173    if (X && T) {
174        duration->seconds = (time_t) atoi(str+1);
175        str = X;
176        not_weeks = 1;
177    }
178
179    W = strchr(str, 'W');
180    if (W) {
181        if (not_weeks) {
182            ldns_duration_cleanup(duration);
183            return NULL;
184        } else {
185            duration->weeks = (time_t) atoi(str+1);
186        }
187    }
188    return duration;
189}
190
191
192/**
193 * Helper func for ldns_duration2string below. If t > 0,
194 * scan print t and c on buf, forwarding buf. Return 0 on success.
195 */
196static inline int dur_scan_print(char **buf, char *eob, char c, time_t t)
197{
198	if (t > 0) {
199		int r = snprintf(*buf, eob - *buf, "%u%c", (unsigned)t, c);
200		if (r < 0 || (*buf += r) >= eob)
201			return -1;
202	}
203	return 0;
204}
205
206/**
207 * Convert a duration to a string.
208 *
209 */
210char*
211ldns_duration2string(const ldns_duration_type* d)
212{
213 	/* Max string size should be 7 * 40 + 3 on a 127 bits machine
214	 * So 300 (< 273) is more than enough.
215	 */
216	char buf[300] = "P0D", *eob = buf + sizeof(buf), *p = buf + 1;
217
218	if (!d)
219		return NULL;
220
221	if (dur_scan_print(&p, eob, 'Y', d->years)
222	||  dur_scan_print(&p, eob, 'M', d->months)
223	||  dur_scan_print(&p, eob, 'W', d->weeks)
224	||  dur_scan_print(&p, eob, 'D', d->days))
225		return NULL;
226
227	if (d->hours || d->minutes || d->seconds) {
228		if (p > (eob - 2))
229			return NULL; /* Error; no space left on buf for 'T' */
230
231		*p++ = 'T'; *p = 0;
232		if (dur_scan_print(&p, eob, 'H', d->hours)
233		||  dur_scan_print(&p, eob, 'M', d->minutes)
234		||  dur_scan_print(&p, eob, 'S', d->seconds))
235			return NULL;
236	}
237	return strdup(buf);
238}
239
240
241/**
242 * Convert a duration to a time.
243 *
244 */
245time_t
246ldns_duration2time(const ldns_duration_type* duration)
247{
248    time_t period = 0;
249
250    if (duration) {
251        period += (duration->seconds);
252        period += (duration->minutes)*60;
253        period += (duration->hours)*3600;
254        period += (duration->days)*86400;
255        period += (duration->weeks)*86400*7;
256        period += (duration->months)*86400*31;
257        period += (duration->years)*86400*365;
258
259        /* [TODO] calculate correct number of days in this month/year */
260	/*
261        if (duration->months || duration->years) {
262        }
263	*/
264    }
265    return period;
266}
267
268
269/**
270 * Clean up duration.
271 *
272 */
273void
274ldns_duration_cleanup(ldns_duration_type* duration)
275{
276    if (!duration) {
277        return;
278    }
279    free(duration);
280    return;
281}
282