vary.c revision 35773
172445Sassar/*- 2233294Sstas * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> 372445Sassar * All rights reserved. 472445Sassar * 5233294Sstas * Redistribution and use in source and binary forms, with or without 672445Sassar * modification, are permitted provided that the following conditions 772445Sassar * are met: 872445Sassar * 1. Redistributions of source code must retain the above copyright 9233294Sstas * notice, this list of conditions and the following disclaimer. 1072445Sassar * 2. Redistributions in binary form must reproduce the above copyright 1172445Sassar * notice, this list of conditions and the following disclaimer in the 12233294Sstas * documentation and/or other materials provided with the distribution. 1372445Sassar * 1472445Sassar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1572445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1772445Sassar * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1872445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1972445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2172445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2272445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2372445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2472445Sassar * SUCH DAMAGE. 2572445Sassar */ 2672445Sassar 2772445Sassar#ifndef lint 2872445Sassarstatic const char rcsid[] = 2972445Sassar "$Id$"; 3072445Sassar#endif /* not lint */ 3172445Sassar 3272445Sassar#include <time.h> 3372445Sassar#include <string.h> 3472445Sassar#include <stdlib.h> 3572445Sassar#include "vary.h" 3672445Sassar 3772445Sassarstruct trans { 3872445Sassar int val; 3972445Sassar char *str; 4072445Sassar}; 4172445Sassar 4272445Sassarstatic struct trans trans_mon[] = { 4372445Sassar { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, 4472445Sassar { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" }, 4572445Sassar { 10, "october" }, { 11, "november" }, { 12, "december" }, 4672445Sassar { -1, NULL } 4772445Sassar}; 4872445Sassar 4972445Sassarstatic struct trans trans_wday[] = { 5072445Sassar { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, 5172445Sassar { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, 5272445Sassar { -1, NULL } 5372445Sassar}; 5472445Sassar 5572445Sassarstatic char digits[] = "0123456789"; 56178825Sdfr 57178825Sdfrstatic int 58178825Sdfrtrans(const struct trans t[], const char *arg) 59178825Sdfr{ 60178825Sdfr int f; 61178825Sdfr 62178825Sdfr for (f = 0; t[f].val != -1; f++) 63178825Sdfr if (!strncasecmp(t[f].str, arg, 3) || 64178825Sdfr !strncasecmp(t[f].str, arg, strlen(t[f].str))) 65178825Sdfr return t[f].val; 66178825Sdfr 67178825Sdfr return -1; 68178825Sdfr} 69178825Sdfr 70178825Sdfrstruct vary * 71103423Snectarvary_append(struct vary *v, char *arg) 72103423Snectar{ 73103423Snectar struct vary *result, **nextp; 74103423Snectar 75103423Snectar if (v) { 76103423Snectar result = v; 77103423Snectar while (v->next) 78103423Snectar v = v->next; 79103423Snectar nextp = &v->next; 80103423Snectar } else 81103423Snectar nextp = &result; 82103423Snectar 83103423Snectar *nextp = (struct vary *)malloc(sizeof(struct vary)); 84103423Snectar (*nextp)->arg = arg; 85103423Snectar (*nextp)->next = NULL; 86103423Snectar return result; 87103423Snectar} 88103423Snectar 89103423Snectarstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 90103423Snectar 91103423Snectarstatic int 92103423Snectardaysinmonth(const struct tm *t) 93103423Snectar{ 94103423Snectar int year; 95233294Sstas 96103423Snectar year = t->tm_year + 1900; 97103423Snectar 98103423Snectar if (t->tm_mon == 1) 99103423Snectar if (!(year % 400)) 100103423Snectar return 29; 101103423Snectar else if (!(year % 100)) 102103423Snectar return 28; 103103423Snectar else if (!(year % 4)) 104103423Snectar return 29; 105103423Snectar else 106103423Snectar return 28; 107103423Snectar else if (t->tm_mon >= 0 && t->tm_mon < 12) 108103423Snectar return mdays[t->tm_mon]; 109103423Snectar 110103423Snectar return 0; 111103423Snectar} 112103423Snectar 113103423Snectar 114103423Snectarstatic int 115103423Snectaradjyear(struct tm *t, char type, int val) 116103423Snectar{ 117103423Snectar switch (type) { 118103423Snectar case '+': 119103423Snectar t->tm_year += val; 120103423Snectar break; 121103423Snectar case '-': 122103423Snectar t->tm_year -= val; 123178825Sdfr break; 124103423Snectar default: 125103423Snectar t->tm_year = val; 126103423Snectar if (t->tm_year < 69) 127103423Snectar t->tm_year += 100; /* as per date.c */ 128103423Snectar else if (t->tm_year > 1900) 129103423Snectar t->tm_year -= 1900; /* struct tm holds years since 1900 */ 130103423Snectar break; 131103423Snectar } 132103423Snectar return mktime(t) != -1; 133103423Snectar} 134103423Snectar 135103423Snectarstatic int 136103423Snectaradjmon(struct tm *t, char type, int val, int istext) 137103423Snectar{ 138103423Snectar if (val < 0) 139103423Snectar return 0; 140103423Snectar 141103423Snectar switch (type) { 142103423Snectar case '+': 143103423Snectar if (istext) 144103423Snectar if (val <= t->tm_mon) 145103423Snectar val += 11 - t->tm_mon; /* early next year */ 146103423Snectar else 147103423Snectar val -= t->tm_mon + 1; /* later this year */ 148103423Snectar if (!adjyear(t, '+', (t->tm_mon + val) / 12)) 149103423Snectar return 0; 150103423Snectar val %= 12; 151103423Snectar t->tm_mon += val; 152103423Snectar if (t->tm_mon > 11) 153103423Snectar t->tm_mon -= 12; 154103423Snectar break; 155103423Snectar 156103423Snectar case '-': 157103423Snectar if (istext) 158103423Snectar if (val-1 > t->tm_mon) 159103423Snectar val = 13 - val + t->tm_mon; /* later last year */ 160103423Snectar else 161103423Snectar val = t->tm_mon - val + 1; /* early this year */ 162103423Snectar if (!adjyear(t, '-', val / 12)) 163103423Snectar return 0; 164103423Snectar val %= 12; 165103423Snectar if (val > t->tm_mon) { 166103423Snectar if (!adjyear(t, '-', 1)) 167103423Snectar return 0; 168103423Snectar val -= 12; 169103423Snectar } 170103423Snectar t->tm_mon -= val; 171103423Snectar break; 172103423Snectar 173103423Snectar default: 174103423Snectar if (val > 12 || val < 1) 175103423Snectar return 0; 176103423Snectar t->tm_mon = --val; 177103423Snectar } 178103423Snectar 179103423Snectar return mktime(t) != -1; 180103423Snectar} 181103423Snectar 182103423Snectarstatic int 183103423Snectaradjday(struct tm *t, char type, int val) 184103423Snectar{ 185103423Snectar int mdays; 186103423Snectar switch (type) { 187103423Snectar case '+': 188178825Sdfr while (val) { 189103423Snectar mdays = daysinmonth(t); 190103423Snectar if (val > mdays - t->tm_mday) { 191103423Snectar val -= mdays - t->tm_mday + 1; 192103423Snectar t->tm_mday = 1; 193233294Sstas if (!adjmon(t, '+', 1, 0)) 194233294Sstas return 0; 195233294Sstas } else { 196103423Snectar t->tm_mday += val; 197103423Snectar val = 0; 198103423Snectar } 199103423Snectar } 200103423Snectar break; 201103423Snectar case '-': 202103423Snectar while (val) 203103423Snectar if (val >= t->tm_mday) { 204103423Snectar val -= t->tm_mday; 205103423Snectar t->tm_mday = 1; 206103423Snectar if (!adjmon(t, '-', 1, 0)) 207103423Snectar return 0; 208103423Snectar t->tm_mday = daysinmonth(t); 209103423Snectar } else { 210103423Snectar t->tm_mday -= val; 211103423Snectar val = 0; 212103423Snectar } 213103423Snectar break; 214103423Snectar default: 215103423Snectar if (val > 0 && val <= daysinmonth(t)) 216103423Snectar t->tm_mday = val; 217103423Snectar else 218103423Snectar return 0; 219103423Snectar break; 220103423Snectar } 221103423Snectar 222103423Snectar return mktime(t) != -1; 223103423Snectar} 224103423Snectar 225103423Snectarstatic int 226103423Snectaradjwday(struct tm *t, char type, int val, int istext) 227233294Sstas{ 228233294Sstas if (val < 0) 229103423Snectar return 0; 230103423Snectar 231103423Snectar switch (type) { 232103423Snectar case '+': 233103423Snectar if (istext) 234103423Snectar if (val < t->tm_wday) 235103423Snectar val = 7 - t->tm_wday + val; /* early next week */ 236103423Snectar else 237103423Snectar val -= t->tm_wday; /* later this week */ 238103423Snectar else 239103423Snectar val *= 7; /* "-W +5" == "5 weeks in the future" */ 240103423Snectar return adjday(t, '+', val); 241103423Snectar case '-': 242103423Snectar if (istext) 243103423Snectar if (val > t->tm_wday) 244103423Snectar val = 7 - val + t->tm_wday; /* later last week */ 245103423Snectar else 246103423Snectar val = t->tm_wday - val; /* early this week */ 247103423Snectar else 248103423Snectar val *= 7; /* "-W -5" == "5 weeks ago" */ 249103423Snectar return adjday(t, '-', val); 250103423Snectar default: 251103423Snectar if (val < t->tm_wday) 252103423Snectar return adjday(t, '-', t->tm_wday - val); 253103423Snectar else if (val > 6) 254103423Snectar return 0; 255103423Snectar else if (val > t->tm_wday) 256103423Snectar return adjday(t, '+', val - t->tm_wday); 257103423Snectar } 258103423Snectar return 1; 259103423Snectar} 260103423Snectar 261103423Snectarstatic int 262103423Snectaradjhour(struct tm *t, char type, int val) 263103423Snectar{ 264103423Snectar if (val < 0) 265103423Snectar return 0; 266103423Snectar 267103423Snectar switch (type) { 268103423Snectar case '+': 269103423Snectar if (!adjday(t, '+', (t->tm_hour + val) / 24)) 270103423Snectar return 0; 271103423Snectar val %= 24; 272103423Snectar t->tm_hour += val; 273103423Snectar if (t->tm_hour > 23) 274233294Sstas t->tm_hour -= 24; 275103423Snectar break; 276103423Snectar 277103423Snectar case '-': 278103423Snectar if (!adjday(t, '-', val / 24)) 279103423Snectar return 0; 280103423Snectar val %= 24; 281103423Snectar if (val > t->tm_hour) { 282103423Snectar if (!adjday(t, '-', 1)) 283103423Snectar return 0; 284103423Snectar val -= 24; 285103423Snectar } 286103423Snectar t->tm_hour -= val; 287103423Snectar break; 288103423Snectar 289103423Snectar default: 290103423Snectar if (val > 23) 291103423Snectar return 0; 292103423Snectar t->tm_hour = val; 293103423Snectar } 294103423Snectar 295103423Snectar return mktime(t) != -1; 296103423Snectar} 297103423Snectar 298103423Snectarstatic int 299103423Snectaradjmin(struct tm *t, char type, int val) 300233294Sstas{ 301233294Sstas if (val < 0) 302233294Sstas return 0; 303103423Snectar 304103423Snectar switch (type) { 305103423Snectar case '+': 306103423Snectar if (!adjhour(t, '+', (t->tm_min + val) / 60)) 307103423Snectar return 0; 308103423Snectar val %= 60; 309103423Snectar t->tm_min += val; 310103423Snectar if (t->tm_min > 59) 311103423Snectar t->tm_min -= 60; 312103423Snectar break; 313103423Snectar 314103423Snectar case '-': 315103423Snectar if (!adjhour(t, '-', val / 60)) 316103423Snectar return 0; 317103423Snectar val %= 60; 318103423Snectar if (val > t->tm_min) { 319103423Snectar if (!adjhour(t, '-', 1)) 320103423Snectar return 0; 321103423Snectar val -= 60; 322103423Snectar } 323103423Snectar t->tm_min -= val; 324103423Snectar break; 325103423Snectar 326103423Snectar default: 327233294Sstas if (val > 59) 328233294Sstas return 0; 329103423Snectar t->tm_min = val; 330103423Snectar } 331103423Snectar 332103423Snectar return mktime(t) != -1; 333103423Snectar} 334103423Snectar 335103423Snectarconst struct vary * 336103423Snectarvary_apply(const struct vary *v, struct tm *t) 337103423Snectar{ 338103423Snectar char type; 339103423Snectar char which; 340103423Snectar char *arg; 341103423Snectar int len; 342103423Snectar int val; 343103423Snectar 344103423Snectar for (; v; v = v->next) { 345103423Snectar type = *v->arg; 346103423Snectar arg = v->arg; 347103423Snectar if (type == '+' || type == '-') 348103423Snectar arg++; 349103423Snectar else 350103423Snectar type = '\0'; 351103423Snectar len = strlen(arg); 352103423Snectar if (len < 2) 353103423Snectar return v; 354103423Snectar 355103423Snectar if (strspn(arg, digits) != len-1) { 356103423Snectar val = trans(trans_wday, arg); 357103423Snectar if (val != -1) { 358103423Snectar if (!adjwday(t, type, val, 1)) 359103423Snectar return v; 360103423Snectar } else { 361103423Snectar val = trans(trans_mon, arg); 362103423Snectar if (val != -1) { 363103423Snectar if (!adjmon(t, type, val, 1)) 364103423Snectar return v; 365103423Snectar } else 366103423Snectar return v; 367103423Snectar } 368103423Snectar } else { 369103423Snectar val = atoi(arg); 370103423Snectar which = arg[len-1]; 371103423Snectar 372103423Snectar switch (which) { 373103423Snectar case 'M': 374103423Snectar if (!adjmin(t, type, val)) 375103423Snectar return v; 376103423Snectar break; 377103423Snectar case 'H': 378103423Snectar if (!adjhour(t, type, val)) 379103423Snectar return v; 380103423Snectar break; 381103423Snectar case 'd': 382103423Snectar if (!adjday(t, type, val)) 383103423Snectar return v; 384103423Snectar break; 38572445Sassar case 'w': 386103423Snectar if (!adjwday(t, type, val, 0)) 387103423Snectar return v; 388103423Snectar break; 389103423Snectar case 'm': 390103423Snectar if (!adjmon(t, type, val, 0)) 391103423Snectar return v; 392103423Snectar break; 393103423Snectar case 'y': 394178825Sdfr if (!adjyear(t, type, val)) 395103423Snectar return v; 396178825Sdfr break; 397103423Snectar default: 398103423Snectar return v; 399103423Snectar } 400103423Snectar } 401103423Snectar } 402103423Snectar return 0; 403178825Sdfr} 404178825Sdfr 405178825Sdfrvoid 406178825Sdfrvary_destroy(struct vary *v) 407178825Sdfr{ 408178825Sdfr struct vary *n; 409178825Sdfr 410178825Sdfr while (v) { 411178825Sdfr n = v->next; 412178825Sdfr free(v); 413178825Sdfr v = n; 414178825Sdfr } 415178825Sdfr} 416178825Sdfr