xref: /freebsd/contrib/ldns/duration.c (revision a0409676120c1e558d0ade943019934e0f15118d)
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  */
53 ldns_duration_type*
54 ldns_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  */
77 int
78 ldns_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  */
117 ldns_duration_type*
118 ldns_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  */
197 static size_t
198 digits_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  */
215 char*
216 ldns_duration2string(const 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  */
318 time_t
319 ldns_duration2time(const 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  */
346 void
347 ldns_duration_cleanup(ldns_duration_type* duration)
348 {
349     if (!duration) {
350         return;
351     }
352     free(duration);
353     return;
354 }
355