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(ldns_duration_type* d1, 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            str = W;
187        }
188    }
189    return duration;
190}
191
192
193/**
194 * Get the number of digits in a number.
195 *
196 */
197static size_t
198digits_in_number(time_t duration)
199{
200    uint32_t period = (uint32_t) duration;
201    size_t count = 0;
202
203    while (period > 0) {
204        count++;
205        period /= 10;
206    }
207    return count;
208}
209
210
211/**
212 * Convert a duration to a string.
213 *
214 */
215char*
216ldns_duration2string(ldns_duration_type* duration)
217{
218    char* str = NULL, *num = NULL;
219    size_t count = 2;
220    int T = 0;
221
222    if (!duration) {
223        return NULL;
224    }
225
226    if (duration->years > 0) {
227        count = count + 1 + digits_in_number(duration->years);
228    }
229    if (duration->months > 0) {
230        count = count + 1 + digits_in_number(duration->months);
231    }
232    if (duration->weeks > 0) {
233        count = count + 1 + digits_in_number(duration->weeks);
234    }
235    if (duration->days > 0) {
236        count = count + 1 + digits_in_number(duration->days);
237    }
238    if (duration->hours > 0) {
239        count = count + 1 + digits_in_number(duration->hours);
240        T = 1;
241    }
242    if (duration->minutes > 0) {
243        count = count + 1 + digits_in_number(duration->minutes);
244        T = 1;
245    }
246    if (duration->seconds > 0) {
247        count = count + 1 + digits_in_number(duration->seconds);
248        T = 1;
249    }
250    if (T) {
251        count++;
252    }
253
254    str = (char*) calloc(count, sizeof(char));
255    str[0] = 'P';
256    str[1] = '\0';
257
258    if (duration->years > 0) {
259        count = digits_in_number(duration->years);
260        num = (char*) calloc(count+2, sizeof(char));
261        snprintf(num, count+2, "%uY", (unsigned int) duration->years);
262        str = strncat(str, num, count+2);
263        free((void*) num);
264    }
265    if (duration->months > 0) {
266        count = digits_in_number(duration->months);
267        num = (char*) calloc(count+2, sizeof(char));
268        snprintf(num, count+2, "%uM", (unsigned int) duration->months);
269        str = strncat(str, num, count+2);
270        free((void*) num);
271    }
272    if (duration->weeks > 0) {
273        count = digits_in_number(duration->weeks);
274        num = (char*) calloc(count+2, sizeof(char));
275        snprintf(num, count+2, "%uW", (unsigned int) duration->weeks);
276        str = strncat(str, num, count+2);
277        free((void*) num);
278    }
279    if (duration->days > 0) {
280        count = digits_in_number(duration->days);
281        num = (char*) calloc(count+2, sizeof(char));
282        snprintf(num, count+2, "%uD", (unsigned int) duration->days);
283        str = strncat(str, num, count+2);
284        free((void*) num);
285    }
286    if (T) {
287        str = strncat(str, "T", 1);
288    }
289    if (duration->hours > 0) {
290        count = digits_in_number(duration->hours);
291        num = (char*) calloc(count+2, sizeof(char));
292        snprintf(num, count+2, "%uH", (unsigned int) duration->hours);
293        str = strncat(str, num, count+2);
294        free((void*) num);
295    }
296    if (duration->minutes > 0) {
297        count = digits_in_number(duration->minutes);
298        num = (char*) calloc(count+2, sizeof(char));
299        snprintf(num, count+2, "%uM", (unsigned int) duration->minutes);
300        str = strncat(str, num, count+2);
301        free((void*) num);
302    }
303    if (duration->seconds > 0) {
304        count = digits_in_number(duration->seconds);
305        num = (char*) calloc(count+2, sizeof(char));
306        snprintf(num, count+2, "%uS", (unsigned int) duration->seconds);
307        str = strncat(str, num, count+2);
308        free((void*) num);
309    }
310    return str;
311}
312
313
314/**
315 * Convert a duration to a time.
316 *
317 */
318time_t
319ldns_duration2time(ldns_duration_type* duration)
320{
321    time_t period = 0;
322
323    if (duration) {
324        period += (duration->seconds);
325        period += (duration->minutes)*60;
326        period += (duration->hours)*3600;
327        period += (duration->days)*86400;
328        period += (duration->weeks)*86400*7;
329        period += (duration->months)*86400*31;
330        period += (duration->years)*86400*365;
331
332        /* [TODO] calculate correct number of days in this month/year */
333	/*
334        if (duration->months || duration->years) {
335        }
336	*/
337    }
338    return period;
339}
340
341
342/**
343 * Clean up duration.
344 *
345 */
346void
347ldns_duration_cleanup(ldns_duration_type* duration)
348{
349    if (!duration) {
350        return;
351    }
352    free(duration);
353    return;
354}
355