17ca215a6SBrian Somers #include <time.h> 27ca215a6SBrian Somers #include <string.h> 37ca215a6SBrian Somers #include <stdlib.h> 47ca215a6SBrian Somers #include "vary.h" 57ca215a6SBrian Somers 67ca215a6SBrian Somers struct trans { 77ca215a6SBrian Somers int val; 87ca215a6SBrian Somers char *str; 97ca215a6SBrian Somers }; 107ca215a6SBrian Somers 117ca215a6SBrian Somers static struct trans trans_mon[] = { 127ca215a6SBrian Somers { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, 137ca215a6SBrian Somers { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" }, 147ca215a6SBrian Somers { 10, "october" }, { 11, "november" }, { 12, "december" }, 157ca215a6SBrian Somers { -1, NULL } 167ca215a6SBrian Somers }; 177ca215a6SBrian Somers 187ca215a6SBrian Somers static struct trans trans_wday[] = { 197ca215a6SBrian Somers { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, 207ca215a6SBrian Somers { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, 217ca215a6SBrian Somers { -1, NULL } 227ca215a6SBrian Somers }; 237ca215a6SBrian Somers 247ca215a6SBrian Somers static char digits[] = "0123456789"; 257ca215a6SBrian Somers 267ca215a6SBrian Somers static int 277ca215a6SBrian Somers trans(const struct trans t[], const char *arg) 287ca215a6SBrian Somers { 297ca215a6SBrian Somers int f; 307ca215a6SBrian Somers 317ca215a6SBrian Somers for (f = 0; t[f].val != -1; f++) 32698f86e4SBrian Somers if (!strncasecmp(t[f].str, arg, 3) || 33698f86e4SBrian Somers !strncasecmp(t[f].str, arg, strlen(t[f].str))) 347ca215a6SBrian Somers return t[f].val; 357ca215a6SBrian Somers 367ca215a6SBrian Somers return -1; 377ca215a6SBrian Somers } 387ca215a6SBrian Somers 397ca215a6SBrian Somers struct vary * 40698f86e4SBrian Somers vary_append(struct vary *v, char *arg) 417ca215a6SBrian Somers { 427ca215a6SBrian Somers struct vary *result, **nextp; 437ca215a6SBrian Somers 447ca215a6SBrian Somers if (v) { 457ca215a6SBrian Somers result = v; 467ca215a6SBrian Somers while (v->next) 477ca215a6SBrian Somers v = v->next; 487ca215a6SBrian Somers nextp = &v->next; 497ca215a6SBrian Somers } else 507ca215a6SBrian Somers nextp = &result; 517ca215a6SBrian Somers 527ca215a6SBrian Somers *nextp = (struct vary *)malloc(sizeof(struct vary)); 537ca215a6SBrian Somers (*nextp)->arg = arg; 547ca215a6SBrian Somers (*nextp)->next = NULL; 557ca215a6SBrian Somers return result; 567ca215a6SBrian Somers } 577ca215a6SBrian Somers 587ca215a6SBrian Somers static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 597ca215a6SBrian Somers 607ca215a6SBrian Somers static int 617ca215a6SBrian Somers daysinmonth(const struct tm *t) 627ca215a6SBrian Somers { 637ca215a6SBrian Somers int year; 647ca215a6SBrian Somers 657ca215a6SBrian Somers year = t->tm_year + 1900; 667ca215a6SBrian Somers 677ca215a6SBrian Somers if (t->tm_mon == 1) 687ca215a6SBrian Somers if (!(year % 400)) 697ca215a6SBrian Somers return 29; 707ca215a6SBrian Somers else if (!(year % 100)) 717ca215a6SBrian Somers return 28; 727ca215a6SBrian Somers else if (!(year % 4)) 737ca215a6SBrian Somers return 29; 747ca215a6SBrian Somers else 757ca215a6SBrian Somers return 28; 767ca215a6SBrian Somers else if (t->tm_mon >= 0 && t->tm_mon < 12) 777ca215a6SBrian Somers return mdays[t->tm_mon]; 787ca215a6SBrian Somers 797ca215a6SBrian Somers return 0; 807ca215a6SBrian Somers } 817ca215a6SBrian Somers 827ca215a6SBrian Somers 837ca215a6SBrian Somers static int 847ca215a6SBrian Somers adjyear(struct tm *t, char type, int val) 857ca215a6SBrian Somers { 867ca215a6SBrian Somers switch (type) { 877ca215a6SBrian Somers case '+': 887ca215a6SBrian Somers t->tm_year += val; 897ca215a6SBrian Somers break; 907ca215a6SBrian Somers case '-': 917ca215a6SBrian Somers t->tm_year -= val; 927ca215a6SBrian Somers break; 937ca215a6SBrian Somers default: 947ca215a6SBrian Somers t->tm_year = val; 957ca215a6SBrian Somers if (t->tm_year < 69) 967ca215a6SBrian Somers t->tm_year += 100; /* as per date.c */ 977ca215a6SBrian Somers else if (t->tm_year > 1900) 987ca215a6SBrian Somers t->tm_year -= 1900; /* struct tm holds years since 1900 */ 997ca215a6SBrian Somers break; 1007ca215a6SBrian Somers } 1017ca215a6SBrian Somers return mktime(t) != -1; 1027ca215a6SBrian Somers } 1037ca215a6SBrian Somers 1047ca215a6SBrian Somers static int 1057ca215a6SBrian Somers adjmon(struct tm *t, char type, int val, int istext) 1067ca215a6SBrian Somers { 1077ca215a6SBrian Somers if (val < 0) 1087ca215a6SBrian Somers return 0; 1097ca215a6SBrian Somers 1107ca215a6SBrian Somers switch (type) { 1117ca215a6SBrian Somers case '+': 1127ca215a6SBrian Somers if (istext) 1137ca215a6SBrian Somers if (val <= t->tm_mon) 1147ca215a6SBrian Somers val += 11 - t->tm_mon; /* early next year */ 1157ca215a6SBrian Somers else 1167ca215a6SBrian Somers val -= t->tm_mon + 1; /* later this year */ 1177ca215a6SBrian Somers if (!adjyear(t, '+', (t->tm_mon + val) / 12)) 1187ca215a6SBrian Somers return 0; 1197ca215a6SBrian Somers val %= 12; 1207ca215a6SBrian Somers t->tm_mon += val; 1217ca215a6SBrian Somers if (t->tm_mon > 11) 1227ca215a6SBrian Somers t->tm_mon -= 12; 1237ca215a6SBrian Somers break; 1247ca215a6SBrian Somers 1257ca215a6SBrian Somers case '-': 1267ca215a6SBrian Somers if (istext) 1277ca215a6SBrian Somers if (val-1 > t->tm_mon) 1287ca215a6SBrian Somers val = 13 - val + t->tm_mon; /* later last year */ 1297ca215a6SBrian Somers else 1307ca215a6SBrian Somers val = t->tm_mon - val + 1; /* early this year */ 1317ca215a6SBrian Somers if (!adjyear(t, '-', val / 12)) 1327ca215a6SBrian Somers return 0; 1337ca215a6SBrian Somers val %= 12; 1347ca215a6SBrian Somers if (val > t->tm_mon) { 1357ca215a6SBrian Somers if (!adjyear(t, '-', 1)) 1367ca215a6SBrian Somers return 0; 1377ca215a6SBrian Somers val -= 12; 1387ca215a6SBrian Somers } 1397ca215a6SBrian Somers t->tm_mon -= val; 1407ca215a6SBrian Somers break; 1417ca215a6SBrian Somers 1427ca215a6SBrian Somers default: 1437ca215a6SBrian Somers if (val > 12 || val < 1) 1447ca215a6SBrian Somers return 0; 1457ca215a6SBrian Somers t->tm_mon = --val; 1467ca215a6SBrian Somers } 1477ca215a6SBrian Somers 1487ca215a6SBrian Somers return mktime(t) != -1; 1497ca215a6SBrian Somers } 1507ca215a6SBrian Somers 1517ca215a6SBrian Somers static int 1527ca215a6SBrian Somers adjday(struct tm *t, char type, int val) 1537ca215a6SBrian Somers { 1547ca215a6SBrian Somers int mdays; 1557ca215a6SBrian Somers switch (type) { 1567ca215a6SBrian Somers case '+': 1577ca215a6SBrian Somers while (val) { 1587ca215a6SBrian Somers mdays = daysinmonth(t); 1597ca215a6SBrian Somers if (val > mdays - t->tm_mday) { 1607ca215a6SBrian Somers val -= mdays - t->tm_mday + 1; 1617ca215a6SBrian Somers t->tm_mday = 1; 1627ca215a6SBrian Somers if (!adjmon(t, '+', 1, 0)) 1637ca215a6SBrian Somers return 0; 1647ca215a6SBrian Somers } else { 1657ca215a6SBrian Somers t->tm_mday += val; 1667ca215a6SBrian Somers val = 0; 1677ca215a6SBrian Somers } 1687ca215a6SBrian Somers } 1697ca215a6SBrian Somers break; 1707ca215a6SBrian Somers case '-': 1717ca215a6SBrian Somers while (val) 1727ca215a6SBrian Somers if (val >= t->tm_mday) { 1737ca215a6SBrian Somers val -= t->tm_mday; 1747ca215a6SBrian Somers t->tm_mday = 1; 1757ca215a6SBrian Somers if (!adjmon(t, '-', 1, 0)) 1767ca215a6SBrian Somers return 0; 1777ca215a6SBrian Somers t->tm_mday = daysinmonth(t); 1787ca215a6SBrian Somers } else { 1797ca215a6SBrian Somers t->tm_mday -= val; 1807ca215a6SBrian Somers val = 0; 1817ca215a6SBrian Somers } 1827ca215a6SBrian Somers break; 1837ca215a6SBrian Somers default: 1847ca215a6SBrian Somers if (val > 0 && val <= daysinmonth(t)) 1857ca215a6SBrian Somers t->tm_mday = val; 1867ca215a6SBrian Somers else 1877ca215a6SBrian Somers return 0; 1887ca215a6SBrian Somers break; 1897ca215a6SBrian Somers } 1907ca215a6SBrian Somers 1917ca215a6SBrian Somers return mktime(t) != -1; 1927ca215a6SBrian Somers } 1937ca215a6SBrian Somers 1947ca215a6SBrian Somers static int 195698f86e4SBrian Somers adjwday(struct tm *t, char type, int val, int istext) 1967ca215a6SBrian Somers { 1977ca215a6SBrian Somers if (val < 0) 1987ca215a6SBrian Somers return 0; 1997ca215a6SBrian Somers 200698f86e4SBrian Somers switch (type) { 2017ca215a6SBrian Somers case '+': 2027ca215a6SBrian Somers if (istext) 2037ca215a6SBrian Somers if (val < t->tm_wday) 2047ca215a6SBrian Somers val = 7 - t->tm_wday + val; /* early next week */ 2057ca215a6SBrian Somers else 2067ca215a6SBrian Somers val -= t->tm_wday; /* later this week */ 2077ca215a6SBrian Somers else 2087ca215a6SBrian Somers val *= 7; /* "-W +5" == "5 weeks in the future" */ 2097ca215a6SBrian Somers return adjday(t, '+', val); 2107ca215a6SBrian Somers case '-': 2117ca215a6SBrian Somers if (istext) 2127ca215a6SBrian Somers if (val > t->tm_wday) 2137ca215a6SBrian Somers val = 7 - val + t->tm_wday; /* later last week */ 2147ca215a6SBrian Somers else 2157ca215a6SBrian Somers val = t->tm_wday - val; /* early this week */ 2167ca215a6SBrian Somers else 2177ca215a6SBrian Somers val *= 7; /* "-W -5" == "5 weeks ago" */ 2187ca215a6SBrian Somers return adjday(t, '-', val); 2197ca215a6SBrian Somers default: 2207ca215a6SBrian Somers if (val < t->tm_wday) 2217ca215a6SBrian Somers return adjday(t, '-', t->tm_wday - val); 2227ca215a6SBrian Somers else if (val > 6) 2237ca215a6SBrian Somers return 0; 2247ca215a6SBrian Somers else if (val > t->tm_wday) 2257ca215a6SBrian Somers return adjday(t, '+', val - t->tm_wday); 2267ca215a6SBrian Somers } 2277ca215a6SBrian Somers return 1; 2287ca215a6SBrian Somers } 2297ca215a6SBrian Somers 2307ca215a6SBrian Somers static int 231698f86e4SBrian Somers adjhour(struct tm *t, char type, int val) 2327ca215a6SBrian Somers { 233698f86e4SBrian Somers if (val < 0) 234698f86e4SBrian Somers return 0; 235698f86e4SBrian Somers 236698f86e4SBrian Somers switch (type) { 2377ca215a6SBrian Somers case '+': 238698f86e4SBrian Somers if (!adjday(t, '+', (t->tm_hour + val) / 24)) 239698f86e4SBrian Somers return 0; 240698f86e4SBrian Somers val %= 24; 241698f86e4SBrian Somers t->tm_hour += val; 242698f86e4SBrian Somers if (t->tm_hour > 23) 243698f86e4SBrian Somers t->tm_hour -= 24; 244698f86e4SBrian Somers break; 245698f86e4SBrian Somers 2467ca215a6SBrian Somers case '-': 247698f86e4SBrian Somers if (!adjday(t, '-', val / 24)) 248698f86e4SBrian Somers return 0; 249698f86e4SBrian Somers val %= 24; 250698f86e4SBrian Somers if (val > t->tm_hour) { 251698f86e4SBrian Somers if (!adjday(t, '-', 1)) 252698f86e4SBrian Somers return 0; 253698f86e4SBrian Somers val -= 24; 2547ca215a6SBrian Somers } 255698f86e4SBrian Somers t->tm_hour -= val; 256698f86e4SBrian Somers break; 257698f86e4SBrian Somers 258698f86e4SBrian Somers default: 259698f86e4SBrian Somers if (val > 23) 260698f86e4SBrian Somers return 0; 261698f86e4SBrian Somers t->tm_hour = val; 262698f86e4SBrian Somers } 263698f86e4SBrian Somers 264698f86e4SBrian Somers return mktime(t) != -1; 265698f86e4SBrian Somers } 266698f86e4SBrian Somers 267698f86e4SBrian Somers static int 268698f86e4SBrian Somers adjmin(struct tm *t, char type, int val) 269698f86e4SBrian Somers { 270698f86e4SBrian Somers if (val < 0) 271698f86e4SBrian Somers return 0; 272698f86e4SBrian Somers 273698f86e4SBrian Somers switch (type) { 274698f86e4SBrian Somers case '+': 275698f86e4SBrian Somers if (!adjhour(t, '+', (t->tm_min + val) / 60)) 276698f86e4SBrian Somers return 0; 277698f86e4SBrian Somers val %= 60; 278698f86e4SBrian Somers t->tm_min += val; 279698f86e4SBrian Somers if (t->tm_min > 59) 280698f86e4SBrian Somers t->tm_min -= 60; 281698f86e4SBrian Somers break; 282698f86e4SBrian Somers 283698f86e4SBrian Somers case '-': 284698f86e4SBrian Somers if (!adjhour(t, '-', val / 60)) 285698f86e4SBrian Somers return 0; 286698f86e4SBrian Somers val %= 60; 287698f86e4SBrian Somers if (val > t->tm_min) { 288698f86e4SBrian Somers if (!adjhour(t, '-', 1)) 289698f86e4SBrian Somers return 0; 290698f86e4SBrian Somers val -= 60; 291698f86e4SBrian Somers } 292698f86e4SBrian Somers t->tm_min -= val; 293698f86e4SBrian Somers break; 294698f86e4SBrian Somers 295698f86e4SBrian Somers default: 296698f86e4SBrian Somers if (val > 59) 297698f86e4SBrian Somers return 0; 298698f86e4SBrian Somers t->tm_min = val; 299698f86e4SBrian Somers } 300698f86e4SBrian Somers 301698f86e4SBrian Somers return mktime(t) != -1; 3027ca215a6SBrian Somers } 3037ca215a6SBrian Somers 3047ca215a6SBrian Somers const struct vary * 3057ca215a6SBrian Somers vary_apply(const struct vary *v, struct tm *t) 3067ca215a6SBrian Somers { 307698f86e4SBrian Somers char type; 308698f86e4SBrian Somers char which; 309698f86e4SBrian Somers char *arg; 310698f86e4SBrian Somers int len; 311698f86e4SBrian Somers int val; 312698f86e4SBrian Somers 3137ca215a6SBrian Somers for (; v; v = v->next) { 314698f86e4SBrian Somers type = *v->arg; 315698f86e4SBrian Somers arg = v->arg; 316698f86e4SBrian Somers if (type == '+' || type == '-') 317698f86e4SBrian Somers arg++; 318698f86e4SBrian Somers else 319698f86e4SBrian Somers type = '\0'; 320698f86e4SBrian Somers len = strlen(arg); 321698f86e4SBrian Somers if (len < 2) 3227ca215a6SBrian Somers return v; 323698f86e4SBrian Somers 324698f86e4SBrian Somers if (strspn(arg, digits) != len-1) { 325698f86e4SBrian Somers val = trans(trans_wday, arg); 326698f86e4SBrian Somers if (val != -1) { 327698f86e4SBrian Somers if (!adjwday(t, type, val, 1)) 3287ca215a6SBrian Somers return v; 329698f86e4SBrian Somers } else { 330698f86e4SBrian Somers val = trans(trans_mon, arg); 331698f86e4SBrian Somers if (val != -1) { 332698f86e4SBrian Somers if (!adjmon(t, type, val, 1)) 333698f86e4SBrian Somers return v; 334698f86e4SBrian Somers } else 335698f86e4SBrian Somers return v; 336698f86e4SBrian Somers } 337698f86e4SBrian Somers } else { 338698f86e4SBrian Somers val = atoi(arg); 339698f86e4SBrian Somers which = arg[len-1]; 340698f86e4SBrian Somers 341698f86e4SBrian Somers switch (which) { 3427ca215a6SBrian Somers case 'M': 343698f86e4SBrian Somers if (!adjmin(t, type, val)) 3447ca215a6SBrian Somers return v; 3457ca215a6SBrian Somers break; 346698f86e4SBrian Somers case 'H': 347698f86e4SBrian Somers if (!adjhour(t, type, val)) 348698f86e4SBrian Somers return v; 349698f86e4SBrian Somers break; 350698f86e4SBrian Somers case 'd': 351698f86e4SBrian Somers if (!adjday(t, type, val)) 352698f86e4SBrian Somers return v; 353698f86e4SBrian Somers break; 354698f86e4SBrian Somers case 'w': 355698f86e4SBrian Somers if (!adjwday(t, type, val, 0)) 356698f86e4SBrian Somers return v; 357698f86e4SBrian Somers break; 358698f86e4SBrian Somers case 'm': 359698f86e4SBrian Somers if (!adjmon(t, type, val, 0)) 360698f86e4SBrian Somers return v; 361698f86e4SBrian Somers break; 362698f86e4SBrian Somers case 'y': 363698f86e4SBrian Somers if (!adjyear(t, type, val)) 3647ca215a6SBrian Somers return v; 3657ca215a6SBrian Somers break; 3667ca215a6SBrian Somers default: 3677ca215a6SBrian Somers return v; 3687ca215a6SBrian Somers } 3697ca215a6SBrian Somers } 370698f86e4SBrian Somers } 3717ca215a6SBrian Somers return 0; 3727ca215a6SBrian Somers } 3737ca215a6SBrian Somers 3747ca215a6SBrian Somers void 3757ca215a6SBrian Somers vary_destroy(struct vary *v) 3767ca215a6SBrian Somers { 3777ca215a6SBrian Somers struct vary *n; 3787ca215a6SBrian Somers 3797ca215a6SBrian Somers while (v) { 3807ca215a6SBrian Somers n = v->next; 3817ca215a6SBrian Somers free(v); 3827ca215a6SBrian Somers v = n; 3837ca215a6SBrian Somers } 3847ca215a6SBrian Somers } 385