1 /*- 2 * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #ifndef lint 28 static const char rcsid[] = 29 "$FreeBSD$"; 30 #endif /* not lint */ 31 32 #include <sys/cdefs.h> 33 #include <err.h> 34 #include <time.h> 35 #include <string.h> 36 #include <stdlib.h> 37 #include "vary.h" 38 39 struct trans { 40 int val; 41 const char *str; 42 }; 43 44 static struct trans trans_mon[] = { 45 { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, 46 { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" }, 47 { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" }, 48 { -1, NULL } 49 }; 50 51 static struct trans trans_wday[] = { 52 { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, 53 { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, 54 { -1, NULL } 55 }; 56 57 static char digits[] = "0123456789"; 58 static int adjhour(struct tm *, char, int, int); 59 60 static int 61 domktime(struct tm *t, char type) 62 { 63 time_t ret; 64 65 while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138) 66 /* While mktime() fails, adjust by an hour */ 67 adjhour(t, type == '-' ? type : '+', 1, 0); 68 69 return ret; 70 } 71 72 static int 73 trans(const struct trans t[], const char *arg) 74 { 75 int f; 76 77 for (f = 0; t[f].val != -1; f++) 78 if (!strncasecmp(t[f].str, arg, 3) || 79 !strncasecmp(t[f].str, arg, strlen(t[f].str))) 80 return t[f].val; 81 82 return -1; 83 } 84 85 struct vary * 86 vary_append(struct vary *v, char *arg) 87 { 88 struct vary *result, **nextp; 89 90 if (v) { 91 result = v; 92 while (v->next) 93 v = v->next; 94 nextp = &v->next; 95 } else 96 nextp = &result; 97 98 if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL) 99 err(1, "malloc"); 100 (*nextp)->arg = arg; 101 (*nextp)->next = NULL; 102 return result; 103 } 104 105 static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 106 107 static int 108 daysinmonth(const struct tm *t) 109 { 110 int year; 111 112 year = t->tm_year + 1900; 113 114 if (t->tm_mon == 1) 115 if (!(year % 400)) 116 return 29; 117 else if (!(year % 100)) 118 return 28; 119 else if (!(year % 4)) 120 return 29; 121 else 122 return 28; 123 else if (t->tm_mon >= 0 && t->tm_mon < 12) 124 return mdays[t->tm_mon]; 125 126 return 0; 127 } 128 129 130 static int 131 adjyear(struct tm *t, char type, int val, int mk) 132 { 133 switch (type) { 134 case '+': 135 t->tm_year += val; 136 break; 137 case '-': 138 t->tm_year -= val; 139 break; 140 default: 141 t->tm_year = val; 142 if (t->tm_year < 69) 143 t->tm_year += 100; /* as per date.c */ 144 else if (t->tm_year > 1900) 145 t->tm_year -= 1900; /* struct tm holds years since 1900 */ 146 break; 147 } 148 return !mk || domktime(t, type) != -1; 149 } 150 151 static int 152 adjmon(struct tm *t, char type, int val, int istext, int mk) 153 { 154 if (val < 0) 155 return 0; 156 157 switch (type) { 158 case '+': 159 if (istext) { 160 if (val <= t->tm_mon) 161 val += 11 - t->tm_mon; /* early next year */ 162 else 163 val -= t->tm_mon + 1; /* later this year */ 164 } 165 if (val) { 166 if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0)) 167 return 0; 168 val %= 12; 169 t->tm_mon += val; 170 if (t->tm_mon > 11) 171 t->tm_mon -= 12; 172 } 173 break; 174 175 case '-': 176 if (istext) { 177 if (val-1 > t->tm_mon) 178 val = 13 - val + t->tm_mon; /* later last year */ 179 else 180 val = t->tm_mon - val + 1; /* early this year */ 181 } 182 if (val) { 183 if (!adjyear(t, '-', val / 12, 0)) 184 return 0; 185 val %= 12; 186 if (val > t->tm_mon) { 187 if (!adjyear(t, '-', 1, 0)) 188 return 0; 189 val -= 12; 190 } 191 t->tm_mon -= val; 192 } 193 break; 194 195 default: 196 if (val > 12 || val < 1) 197 return 0; 198 t->tm_mon = --val; 199 } 200 201 return !mk || domktime(t, type) != -1; 202 } 203 204 static int 205 adjday(struct tm *t, char type, int val, int mk) 206 { 207 int lmdays; 208 209 switch (type) { 210 case '+': 211 while (val) { 212 lmdays = daysinmonth(t); 213 if (val > lmdays - t->tm_mday) { 214 val -= lmdays - t->tm_mday + 1; 215 t->tm_mday = 1; 216 if (!adjmon(t, '+', 1, 0, 0)) 217 return 0; 218 } else { 219 t->tm_mday += val; 220 val = 0; 221 } 222 } 223 break; 224 case '-': 225 while (val) 226 if (val >= t->tm_mday) { 227 val -= t->tm_mday; 228 t->tm_mday = 1; 229 if (!adjmon(t, '-', 1, 0, 0)) 230 return 0; 231 t->tm_mday = daysinmonth(t); 232 } else { 233 t->tm_mday -= val; 234 val = 0; 235 } 236 break; 237 default: 238 if (val > 0 && val <= daysinmonth(t)) 239 t->tm_mday = val; 240 else 241 return 0; 242 break; 243 } 244 245 return !mk || domktime(t, type) != -1; 246 } 247 248 static int 249 adjwday(struct tm *t, char type, int val, int istext, int mk) 250 { 251 if (val < 0) 252 return 0; 253 254 switch (type) { 255 case '+': 256 if (istext) 257 if (val < t->tm_wday) 258 val = 7 - t->tm_wday + val; /* early next week */ 259 else 260 val -= t->tm_wday; /* later this week */ 261 else 262 val *= 7; /* "-v+5w" == "5 weeks in the future" */ 263 return !val || adjday(t, '+', val, mk); 264 case '-': 265 if (istext) { 266 if (val > t->tm_wday) 267 val = 7 - val + t->tm_wday; /* later last week */ 268 else 269 val = t->tm_wday - val; /* early this week */ 270 } else 271 val *= 7; /* "-v-5w" == "5 weeks ago" */ 272 return !val || adjday(t, '-', val, mk); 273 default: 274 if (val < t->tm_wday) 275 return adjday(t, '-', t->tm_wday - val, mk); 276 else if (val > 6) 277 return 0; 278 else if (val > t->tm_wday) 279 return adjday(t, '+', val - t->tm_wday, mk); 280 } 281 return 1; 282 } 283 284 static int 285 adjhour(struct tm *t, char type, int val, int mk) 286 { 287 if (val < 0) 288 return 0; 289 290 switch (type) { 291 case '+': 292 if (val) { 293 int days; 294 295 days = (t->tm_hour + val) / 24; 296 val %= 24; 297 t->tm_hour += val; 298 t->tm_hour %= 24; 299 if (!adjday(t, '+', days, 0)) 300 return 0; 301 } 302 break; 303 304 case '-': 305 if (val) { 306 int days; 307 308 days = val / 24; 309 val %= 24; 310 if (val > t->tm_hour) { 311 days++; 312 val -= 24; 313 } 314 t->tm_hour -= val; 315 if (!adjday(t, '-', days, 0)) 316 return 0; 317 } 318 break; 319 320 default: 321 if (val > 23) 322 return 0; 323 t->tm_hour = val; 324 } 325 326 return !mk || domktime(t, type) != -1; 327 } 328 329 static int 330 adjmin(struct tm *t, char type, int val, int mk) 331 { 332 if (val < 0) 333 return 0; 334 335 switch (type) { 336 case '+': 337 if (val) { 338 if (!adjhour(t, '+', (t->tm_min + val) / 60, 0)) 339 return 0; 340 val %= 60; 341 t->tm_min += val; 342 if (t->tm_min > 59) 343 t->tm_min -= 60; 344 } 345 break; 346 347 case '-': 348 if (val) { 349 if (!adjhour(t, '-', val / 60, 0)) 350 return 0; 351 val %= 60; 352 if (val > t->tm_min) { 353 if (!adjhour(t, '-', 1, 0)) 354 return 0; 355 val -= 60; 356 } 357 t->tm_min -= val; 358 } 359 break; 360 361 default: 362 if (val > 59) 363 return 0; 364 t->tm_min = val; 365 } 366 367 return !mk || domktime(t, type) != -1; 368 } 369 370 static int 371 adjsec(struct tm *t, char type, int val, int mk) 372 { 373 if (val < 0) 374 return 0; 375 376 switch (type) { 377 case '+': 378 if (val) { 379 if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0)) 380 return 0; 381 val %= 60; 382 t->tm_sec += val; 383 if (t->tm_sec > 59) 384 t->tm_sec -= 60; 385 } 386 break; 387 388 case '-': 389 if (val) { 390 if (!adjmin(t, '-', val / 60, 0)) 391 return 0; 392 val %= 60; 393 if (val > t->tm_sec) { 394 if (!adjmin(t, '-', 1, 0)) 395 return 0; 396 val -= 60; 397 } 398 t->tm_sec -= val; 399 } 400 break; 401 402 default: 403 if (val > 59) 404 return 0; 405 t->tm_sec = val; 406 } 407 408 return !mk || domktime(t, type) != -1; 409 } 410 411 const struct vary * 412 vary_apply(const struct vary *v, struct tm *t) 413 { 414 char type; 415 char which; 416 char *arg; 417 size_t len; 418 int val; 419 420 for (; v; v = v->next) { 421 type = *v->arg; 422 arg = v->arg; 423 if (type == '+' || type == '-') 424 arg++; 425 else 426 type = '\0'; 427 len = strlen(arg); 428 if (len < 2) 429 return v; 430 431 if (type == '\0') 432 t->tm_isdst = -1; 433 434 if (strspn(arg, digits) != len-1) { 435 val = trans(trans_wday, arg); 436 if (val != -1) { 437 if (!adjwday(t, type, val, 1, 1)) 438 return v; 439 } else { 440 val = trans(trans_mon, arg); 441 if (val != -1) { 442 if (!adjmon(t, type, val, 1, 1)) 443 return v; 444 } else 445 return v; 446 } 447 } else { 448 val = atoi(arg); 449 which = arg[len-1]; 450 451 switch (which) { 452 case 'S': 453 if (!adjsec(t, type, val, 1)) 454 return v; 455 break; 456 case 'M': 457 if (!adjmin(t, type, val, 1)) 458 return v; 459 break; 460 case 'H': 461 if (!adjhour(t, type, val, 1)) 462 return v; 463 break; 464 case 'd': 465 t->tm_isdst = -1; 466 if (!adjday(t, type, val, 1)) 467 return v; 468 break; 469 case 'w': 470 t->tm_isdst = -1; 471 if (!adjwday(t, type, val, 0, 1)) 472 return v; 473 break; 474 case 'm': 475 t->tm_isdst = -1; 476 if (!adjmon(t, type, val, 0, 1)) 477 return v; 478 break; 479 case 'y': 480 t->tm_isdst = -1; 481 if (!adjyear(t, type, val, 1)) 482 return v; 483 break; 484 default: 485 return v; 486 } 487 } 488 } 489 return 0; 490 } 491 492 void 493 vary_destroy(struct vary *v) 494 { 495 struct vary *n; 496 497 while (v) { 498 n = v->next; 499 free(v); 500 v = n; 501 } 502 } 503