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