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 <time.h> 33 #include <string.h> 34 #include <stdlib.h> 35 #include "vary.h" 36 37 struct trans { 38 int val; 39 char *str; 40 }; 41 42 static struct trans trans_mon[] = { 43 { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, 44 { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" }, 45 { 10, "october" }, { 11, "november" }, { 12, "december" }, 46 { -1, NULL } 47 }; 48 49 static struct trans trans_wday[] = { 50 { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, 51 { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, 52 { -1, NULL } 53 }; 54 55 static char digits[] = "0123456789"; 56 57 static int 58 trans(const struct trans t[], const char *arg) 59 { 60 int f; 61 62 for (f = 0; t[f].val != -1; f++) 63 if (!strncasecmp(t[f].str, arg, 3) || 64 !strncasecmp(t[f].str, arg, strlen(t[f].str))) 65 return t[f].val; 66 67 return -1; 68 } 69 70 struct vary * 71 vary_append(struct vary *v, char *arg) 72 { 73 struct vary *result, **nextp; 74 75 if (v) { 76 result = v; 77 while (v->next) 78 v = v->next; 79 nextp = &v->next; 80 } else 81 nextp = &result; 82 83 *nextp = (struct vary *)malloc(sizeof(struct vary)); 84 (*nextp)->arg = arg; 85 (*nextp)->next = NULL; 86 return result; 87 } 88 89 static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 90 91 static int 92 daysinmonth(const struct tm *t) 93 { 94 int year; 95 96 year = t->tm_year + 1900; 97 98 if (t->tm_mon == 1) 99 if (!(year % 400)) 100 return 29; 101 else if (!(year % 100)) 102 return 28; 103 else if (!(year % 4)) 104 return 29; 105 else 106 return 28; 107 else if (t->tm_mon >= 0 && t->tm_mon < 12) 108 return mdays[t->tm_mon]; 109 110 return 0; 111 } 112 113 114 static int 115 adjyear(struct tm *t, char type, int val) 116 { 117 switch (type) { 118 case '+': 119 t->tm_year += val; 120 break; 121 case '-': 122 t->tm_year -= val; 123 break; 124 default: 125 t->tm_year = val; 126 if (t->tm_year < 69) 127 t->tm_year += 100; /* as per date.c */ 128 else if (t->tm_year > 1900) 129 t->tm_year -= 1900; /* struct tm holds years since 1900 */ 130 break; 131 } 132 return mktime(t) != -1; 133 } 134 135 static int 136 adjmon(struct tm *t, char type, int val, int istext) 137 { 138 if (val < 0) 139 return 0; 140 141 switch (type) { 142 case '+': 143 if (istext) { 144 if (val <= t->tm_mon) 145 val += 11 - t->tm_mon; /* early next year */ 146 else 147 val -= t->tm_mon + 1; /* later this year */ 148 } 149 if (!adjyear(t, '+', (t->tm_mon + val) / 12)) 150 return 0; 151 val %= 12; 152 t->tm_mon += val; 153 if (t->tm_mon > 11) 154 t->tm_mon -= 12; 155 break; 156 157 case '-': 158 if (istext) { 159 if (val-1 > t->tm_mon) 160 val = 13 - val + t->tm_mon; /* later last year */ 161 else 162 val = t->tm_mon - val + 1; /* early this year */ 163 } 164 if (!adjyear(t, '-', val / 12)) 165 return 0; 166 val %= 12; 167 if (val > t->tm_mon) { 168 if (!adjyear(t, '-', 1)) 169 return 0; 170 val -= 12; 171 } 172 t->tm_mon -= val; 173 break; 174 175 default: 176 if (val > 12 || val < 1) 177 return 0; 178 t->tm_mon = --val; 179 } 180 181 return mktime(t) != -1; 182 } 183 184 static int 185 adjday(struct tm *t, char type, int val) 186 { 187 int mdays; 188 switch (type) { 189 case '+': 190 while (val) { 191 mdays = daysinmonth(t); 192 if (val > mdays - t->tm_mday) { 193 val -= mdays - t->tm_mday + 1; 194 t->tm_mday = 1; 195 if (!adjmon(t, '+', 1, 0)) 196 return 0; 197 } else { 198 t->tm_mday += val; 199 val = 0; 200 } 201 } 202 break; 203 case '-': 204 while (val) 205 if (val >= t->tm_mday) { 206 val -= t->tm_mday; 207 t->tm_mday = 1; 208 if (!adjmon(t, '-', 1, 0)) 209 return 0; 210 t->tm_mday = daysinmonth(t); 211 } else { 212 t->tm_mday -= val; 213 val = 0; 214 } 215 break; 216 default: 217 if (val > 0 && val <= daysinmonth(t)) 218 t->tm_mday = val; 219 else 220 return 0; 221 break; 222 } 223 224 return mktime(t) != -1; 225 } 226 227 static int 228 adjwday(struct tm *t, char type, int val, int istext) 229 { 230 if (val < 0) 231 return 0; 232 233 switch (type) { 234 case '+': 235 if (istext) 236 if (val < t->tm_wday) 237 val = 7 - t->tm_wday + val; /* early next week */ 238 else 239 val -= t->tm_wday; /* later this week */ 240 else 241 val *= 7; /* "-W +5" == "5 weeks in the future" */ 242 return adjday(t, '+', val); 243 case '-': 244 if (istext) 245 if (val > t->tm_wday) 246 val = 7 - val + t->tm_wday; /* later last week */ 247 else 248 val = t->tm_wday - val; /* early this week */ 249 else 250 val *= 7; /* "-W -5" == "5 weeks ago" */ 251 return adjday(t, '-', val); 252 default: 253 if (val < t->tm_wday) 254 return adjday(t, '-', t->tm_wday - val); 255 else if (val > 6) 256 return 0; 257 else if (val > t->tm_wday) 258 return adjday(t, '+', val - t->tm_wday); 259 } 260 return 1; 261 } 262 263 static int 264 adjhour(struct tm *t, char type, int val) 265 { 266 if (val < 0) 267 return 0; 268 269 switch (type) { 270 case '+': 271 if (!adjday(t, '+', (t->tm_hour + val) / 24)) 272 return 0; 273 val %= 24; 274 t->tm_hour += val; 275 if (t->tm_hour > 23) 276 t->tm_hour -= 24; 277 break; 278 279 case '-': 280 if (!adjday(t, '-', val / 24)) 281 return 0; 282 val %= 24; 283 if (val > t->tm_hour) { 284 if (!adjday(t, '-', 1)) 285 return 0; 286 val -= 24; 287 } 288 t->tm_hour -= val; 289 break; 290 291 default: 292 if (val > 23) 293 return 0; 294 t->tm_hour = val; 295 } 296 297 return mktime(t) != -1; 298 } 299 300 static int 301 adjmin(struct tm *t, char type, int val) 302 { 303 if (val < 0) 304 return 0; 305 306 switch (type) { 307 case '+': 308 if (!adjhour(t, '+', (t->tm_min + val) / 60)) 309 return 0; 310 val %= 60; 311 t->tm_min += val; 312 if (t->tm_min > 59) 313 t->tm_min -= 60; 314 break; 315 316 case '-': 317 if (!adjhour(t, '-', val / 60)) 318 return 0; 319 val %= 60; 320 if (val > t->tm_min) { 321 if (!adjhour(t, '-', 1)) 322 return 0; 323 val -= 60; 324 } 325 t->tm_min -= val; 326 break; 327 328 default: 329 if (val > 59) 330 return 0; 331 t->tm_min = val; 332 } 333 334 return mktime(t) != -1; 335 } 336 337 static int 338 adjsec(struct tm *t, char type, int val) 339 { 340 if (val < 0) 341 return 0; 342 343 switch (type) { 344 case '+': 345 if (!adjmin(t, '+', (t->tm_sec + val) / 60)) 346 return 0; 347 val %= 60; 348 t->tm_sec += val; 349 if (t->tm_sec > 59) 350 t->tm_sec -= 60; 351 break; 352 353 case '-': 354 if (!adjmin(t, '-', val / 60)) 355 return 0; 356 val %= 60; 357 if (val > t->tm_sec) { 358 if (!adjmin(t, '-', 1)) 359 return 0; 360 val -= 60; 361 } 362 t->tm_sec -= val; 363 break; 364 365 default: 366 if (val > 59) 367 return 0; 368 t->tm_sec = val; 369 } 370 371 return mktime(t) != -1; 372 } 373 374 const struct vary * 375 vary_apply(const struct vary *v, struct tm *t) 376 { 377 char type; 378 char which; 379 char *arg; 380 int len; 381 int val; 382 383 for (; v; v = v->next) { 384 type = *v->arg; 385 arg = v->arg; 386 if (type == '+' || type == '-') 387 arg++; 388 else 389 type = '\0'; 390 len = strlen(arg); 391 if (len < 2) 392 return v; 393 394 if (strspn(arg, digits) != len-1) { 395 val = trans(trans_wday, arg); 396 if (val != -1) { 397 if (!adjwday(t, type, val, 1)) 398 return v; 399 } else { 400 val = trans(trans_mon, arg); 401 if (val != -1) { 402 if (!adjmon(t, type, val, 1)) 403 return v; 404 } else 405 return v; 406 } 407 } else { 408 val = atoi(arg); 409 which = arg[len-1]; 410 411 switch (which) { 412 case 'S': 413 if (!adjsec(t, type, val)) 414 return v; 415 break; 416 case 'M': 417 if (!adjmin(t, type, val)) 418 return v; 419 break; 420 case 'H': 421 if (!adjhour(t, type, val)) 422 return v; 423 break; 424 case 'd': 425 if (!adjday(t, type, val)) 426 return v; 427 break; 428 case 'w': 429 if (!adjwday(t, type, val, 0)) 430 return v; 431 break; 432 case 'm': 433 if (!adjmon(t, type, val, 0)) 434 return v; 435 break; 436 case 'y': 437 if (!adjyear(t, type, val)) 438 return v; 439 break; 440 default: 441 return v; 442 } 443 } 444 } 445 return 0; 446 } 447 448 void 449 vary_destroy(struct vary *v) 450 { 451 struct vary *n; 452 453 while (v) { 454 n = v->next; 455 free(v); 456 v = n; 457 } 458 } 459