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