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