vary.c revision 59175
131921Sbrian/*- 231921Sbrian * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> 331921Sbrian * All rights reserved. 431921Sbrian * 531921Sbrian * Redistribution and use in source and binary forms, with or without 631921Sbrian * modification, are permitted provided that the following conditions 731921Sbrian * are met: 831921Sbrian * 1. Redistributions of source code must retain the above copyright 931921Sbrian * notice, this list of conditions and the following disclaimer. 1031921Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1131921Sbrian * notice, this list of conditions and the following disclaimer in the 1231921Sbrian * documentation and/or other materials provided with the distribution. 1331921Sbrian * 1431921Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1531921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1631921Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1731921Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1831921Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1931921Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2031921Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2131921Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2231921Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2331921Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2431921Sbrian * SUCH DAMAGE. 2531921Sbrian */ 2631921Sbrian 2735773Scharnier#ifndef lint 2835773Scharnierstatic const char rcsid[] = 2950471Speter "$FreeBSD: head/bin/date/vary.c 59175 2000-04-12 13:35:17Z brian $"; 3035773Scharnier#endif /* not lint */ 3135773Scharnier 3227874Sbrian#include <time.h> 3327874Sbrian#include <string.h> 3427874Sbrian#include <stdlib.h> 3527874Sbrian#include "vary.h" 3627874Sbrian 3727874Sbrianstruct trans { 3827874Sbrian int val; 3927874Sbrian char *str; 4027874Sbrian}; 4127874Sbrian 4227874Sbrianstatic struct trans trans_mon[] = { 4327874Sbrian { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, 4457326Salfred { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" }, 4557326Salfred { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" }, 4627874Sbrian { -1, NULL } 4727874Sbrian}; 4827874Sbrian 4927874Sbrianstatic struct trans trans_wday[] = { 5027874Sbrian { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, 5127874Sbrian { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, 5227874Sbrian { -1, NULL } 5327874Sbrian}; 5427874Sbrian 5527874Sbrianstatic char digits[] = "0123456789"; 5659175Sbrianstatic int adjhour(struct tm *, char, int, int); 5727874Sbrian 5827874Sbrianstatic int 5959022Sbriandomktime(struct tm *t, char type) 6059022Sbrian{ 6159175Sbrian time_t ret; 6259022Sbrian 6359175Sbrian while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138) 6459175Sbrian /* While mktime() fails, adjust by an hour */ 6559175Sbrian adjhour(t, type == '-' ? type : '+', 1, 0); 6659022Sbrian 6759022Sbrian return ret; 6859022Sbrian} 6959022Sbrian 7059022Sbrianstatic int 7127874Sbriantrans(const struct trans t[], const char *arg) 7227874Sbrian{ 7327874Sbrian int f; 7427874Sbrian 7527874Sbrian for (f = 0; t[f].val != -1; f++) 7628025Sbrian if (!strncasecmp(t[f].str, arg, 3) || 7728025Sbrian !strncasecmp(t[f].str, arg, strlen(t[f].str))) 7827874Sbrian return t[f].val; 7927874Sbrian 8027874Sbrian return -1; 8127874Sbrian} 8227874Sbrian 8327874Sbrianstruct vary * 8428025Sbrianvary_append(struct vary *v, char *arg) 8527874Sbrian{ 8627874Sbrian struct vary *result, **nextp; 8727874Sbrian 8827874Sbrian if (v) { 8927874Sbrian result = v; 9027874Sbrian while (v->next) 9127874Sbrian v = v->next; 9227874Sbrian nextp = &v->next; 9327874Sbrian } else 9427874Sbrian nextp = &result; 9527874Sbrian 9627874Sbrian *nextp = (struct vary *)malloc(sizeof(struct vary)); 9727874Sbrian (*nextp)->arg = arg; 9827874Sbrian (*nextp)->next = NULL; 9927874Sbrian return result; 10027874Sbrian} 10127874Sbrian 10227874Sbrianstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 10327874Sbrian 10427874Sbrianstatic int 10527874Sbriandaysinmonth(const struct tm *t) 10627874Sbrian{ 10727874Sbrian int year; 10827874Sbrian 10927874Sbrian year = t->tm_year + 1900; 11027874Sbrian 11127874Sbrian if (t->tm_mon == 1) 11227874Sbrian if (!(year % 400)) 11327874Sbrian return 29; 11427874Sbrian else if (!(year % 100)) 11527874Sbrian return 28; 11627874Sbrian else if (!(year % 4)) 11727874Sbrian return 29; 11827874Sbrian else 11927874Sbrian return 28; 12027874Sbrian else if (t->tm_mon >= 0 && t->tm_mon < 12) 12127874Sbrian return mdays[t->tm_mon]; 12227874Sbrian 12327874Sbrian return 0; 12427874Sbrian} 12527874Sbrian 12627874Sbrian 12727874Sbrianstatic int 12859175Sbrianadjyear(struct tm *t, char type, int val, int mk) 12927874Sbrian{ 13027874Sbrian switch (type) { 13127874Sbrian case '+': 13227874Sbrian t->tm_year += val; 13327874Sbrian break; 13427874Sbrian case '-': 13527874Sbrian t->tm_year -= val; 13627874Sbrian break; 13727874Sbrian default: 13827874Sbrian t->tm_year = val; 13927874Sbrian if (t->tm_year < 69) 14027874Sbrian t->tm_year += 100; /* as per date.c */ 14127874Sbrian else if (t->tm_year > 1900) 14227874Sbrian t->tm_year -= 1900; /* struct tm holds years since 1900 */ 14327874Sbrian break; 14427874Sbrian } 14559175Sbrian return !mk || domktime(t, type) != -1; 14627874Sbrian} 14727874Sbrian 14827874Sbrianstatic int 14959175Sbrianadjmon(struct tm *t, char type, int val, int istext, int mk) 15027874Sbrian{ 15127874Sbrian if (val < 0) 15227874Sbrian return 0; 15327874Sbrian 15427874Sbrian switch (type) { 15527874Sbrian case '+': 15646073Simp if (istext) { 15727874Sbrian if (val <= t->tm_mon) 15827874Sbrian val += 11 - t->tm_mon; /* early next year */ 15927874Sbrian else 16027874Sbrian val -= t->tm_mon + 1; /* later this year */ 16146073Simp } 16259022Sbrian if (val) { 16359175Sbrian if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0)) 16459022Sbrian return 0; 16559022Sbrian val %= 12; 16659022Sbrian t->tm_mon += val; 16759022Sbrian if (t->tm_mon > 11) 16859022Sbrian t->tm_mon -= 12; 16959022Sbrian } 17027874Sbrian break; 17127874Sbrian 17227874Sbrian case '-': 17346073Simp if (istext) { 17427874Sbrian if (val-1 > t->tm_mon) 17527874Sbrian val = 13 - val + t->tm_mon; /* later last year */ 17627874Sbrian else 17727874Sbrian val = t->tm_mon - val + 1; /* early this year */ 17846073Simp } 17959022Sbrian if (val) { 18059175Sbrian if (!adjyear(t, '-', val / 12, 0)) 18127874Sbrian return 0; 18259022Sbrian val %= 12; 18359022Sbrian if (val > t->tm_mon) { 18459175Sbrian if (!adjyear(t, '-', 1, 0)) 18559022Sbrian return 0; 18659022Sbrian val -= 12; 18759022Sbrian } 18859022Sbrian t->tm_mon -= val; 18927874Sbrian } 19027874Sbrian break; 19127874Sbrian 19227874Sbrian default: 19327874Sbrian if (val > 12 || val < 1) 19427874Sbrian return 0; 19527874Sbrian t->tm_mon = --val; 19627874Sbrian } 19727874Sbrian 19859175Sbrian return !mk || domktime(t, type) != -1; 19927874Sbrian} 20027874Sbrian 20127874Sbrianstatic int 20259175Sbrianadjday(struct tm *t, char type, int val, int mk) 20327874Sbrian{ 20427874Sbrian int mdays; 20559022Sbrian 20627874Sbrian switch (type) { 20727874Sbrian case '+': 20827874Sbrian while (val) { 20927874Sbrian mdays = daysinmonth(t); 21027874Sbrian if (val > mdays - t->tm_mday) { 21127874Sbrian val -= mdays - t->tm_mday + 1; 21227874Sbrian t->tm_mday = 1; 21359175Sbrian if (!adjmon(t, '+', 1, 0, 0)) 21427874Sbrian return 0; 21527874Sbrian } else { 21627874Sbrian t->tm_mday += val; 21727874Sbrian val = 0; 21827874Sbrian } 21927874Sbrian } 22027874Sbrian break; 22127874Sbrian case '-': 22227874Sbrian while (val) 22327874Sbrian if (val >= t->tm_mday) { 22427874Sbrian val -= t->tm_mday; 22527874Sbrian t->tm_mday = 1; 22659175Sbrian if (!adjmon(t, '-', 1, 0, 0)) 22727874Sbrian return 0; 22827874Sbrian t->tm_mday = daysinmonth(t); 22927874Sbrian } else { 23027874Sbrian t->tm_mday -= val; 23127874Sbrian val = 0; 23227874Sbrian } 23327874Sbrian break; 23427874Sbrian default: 23527874Sbrian if (val > 0 && val <= daysinmonth(t)) 23627874Sbrian t->tm_mday = val; 23727874Sbrian else 23827874Sbrian return 0; 23927874Sbrian break; 24027874Sbrian } 24127874Sbrian 24259175Sbrian return !mk || domktime(t, type) != -1; 24327874Sbrian} 24427874Sbrian 24527874Sbrianstatic int 24659175Sbrianadjwday(struct tm *t, char type, int val, int istext, int mk) 24727874Sbrian{ 24827874Sbrian if (val < 0) 24927874Sbrian return 0; 25027874Sbrian 25128025Sbrian switch (type) { 25227874Sbrian case '+': 25327874Sbrian if (istext) 25427874Sbrian if (val < t->tm_wday) 25527874Sbrian val = 7 - t->tm_wday + val; /* early next week */ 25627874Sbrian else 25727874Sbrian val -= t->tm_wday; /* later this week */ 25827874Sbrian else 25959022Sbrian val *= 7; /* "-v+5w" == "5 weeks in the future" */ 26059175Sbrian return !val || adjday(t, '+', val, 0); 26127874Sbrian case '-': 26259022Sbrian if (istext) { 26327874Sbrian if (val > t->tm_wday) 26427874Sbrian val = 7 - val + t->tm_wday; /* later last week */ 26527874Sbrian else 26627874Sbrian val = t->tm_wday - val; /* early this week */ 26759022Sbrian } else 26859022Sbrian val *= 7; /* "-v-5w" == "5 weeks ago" */ 26959175Sbrian return !val || adjday(t, '-', val, 0); 27027874Sbrian default: 27127874Sbrian if (val < t->tm_wday) 27259175Sbrian return adjday(t, '-', t->tm_wday - val, 0); 27327874Sbrian else if (val > 6) 27427874Sbrian return 0; 27527874Sbrian else if (val > t->tm_wday) 27659175Sbrian return adjday(t, '+', val - t->tm_wday, 0); 27727874Sbrian } 27827874Sbrian return 1; 27927874Sbrian} 28027874Sbrian 28127874Sbrianstatic int 28259175Sbrianadjhour(struct tm *t, char type, int val, int mk) 28327874Sbrian{ 28428025Sbrian if (val < 0) 28528025Sbrian return 0; 28628025Sbrian 28728025Sbrian switch (type) { 28827874Sbrian case '+': 28959022Sbrian if (val) { 29059022Sbrian int days; 29159022Sbrian 29259022Sbrian days = (t->tm_hour + val) / 24; 29359022Sbrian val %= 24; 29459022Sbrian t->tm_hour += val; 29559022Sbrian t->tm_hour %= 24; 29659175Sbrian if (!adjday(t, '+', days, 0)) 29759022Sbrian return 0; 29859022Sbrian } 29928025Sbrian break; 30028025Sbrian 30127874Sbrian case '-': 30259022Sbrian if (val) { 30359022Sbrian int days; 30459022Sbrian 30559022Sbrian days = val / 24; 30659022Sbrian val %= 24; 30759022Sbrian if (val > t->tm_hour) { 30859022Sbrian days++; 30959022Sbrian val -= 24; 31059022Sbrian } 31159022Sbrian t->tm_hour -= val; 31259175Sbrian if (!adjday(t, '-', days, 0)) 31328025Sbrian return 0; 31428025Sbrian } 31528025Sbrian break; 31628025Sbrian 31727874Sbrian default: 31828025Sbrian if (val > 23) 31928025Sbrian return 0; 32028025Sbrian t->tm_hour = val; 32127874Sbrian } 32228025Sbrian 32359175Sbrian return !mk || domktime(t, type) != -1; 32427874Sbrian} 32527874Sbrian 32628025Sbrianstatic int 32759175Sbrianadjmin(struct tm *t, char type, int val, int mk) 32828025Sbrian{ 32928025Sbrian if (val < 0) 33028025Sbrian return 0; 33128025Sbrian 33228025Sbrian switch (type) { 33328025Sbrian case '+': 33459022Sbrian if (val) { 33559175Sbrian if (!adjhour(t, '+', (t->tm_min + val) / 60, 0)) 33659022Sbrian return 0; 33759022Sbrian val %= 60; 33859022Sbrian t->tm_min += val; 33959022Sbrian if (t->tm_min > 59) 34059022Sbrian t->tm_min -= 60; 34159022Sbrian } 34228025Sbrian break; 34328025Sbrian 34428025Sbrian case '-': 34559022Sbrian if (val) { 34659175Sbrian if (!adjhour(t, '-', val / 60, 0)) 34728025Sbrian return 0; 34859022Sbrian val %= 60; 34959022Sbrian if (val > t->tm_min) { 35059175Sbrian if (!adjhour(t, '-', 1, 0)) 35159022Sbrian return 0; 35259022Sbrian val -= 60; 35359022Sbrian } 35459022Sbrian t->tm_min -= val; 35528025Sbrian } 35628025Sbrian break; 35728025Sbrian 35828025Sbrian default: 35928025Sbrian if (val > 59) 36028025Sbrian return 0; 36128025Sbrian t->tm_min = val; 36228025Sbrian } 36328025Sbrian 36459175Sbrian return !mk || domktime(t, type) != -1; 36528025Sbrian} 36628025Sbrian 36744598Sbrianstatic int 36859175Sbrianadjsec(struct tm *t, char type, int val, int mk) 36944598Sbrian{ 37044598Sbrian if (val < 0) 37144598Sbrian return 0; 37244598Sbrian 37344598Sbrian switch (type) { 37444598Sbrian case '+': 37559022Sbrian if (val) { 37659175Sbrian if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0)) 37759022Sbrian return 0; 37859022Sbrian val %= 60; 37959022Sbrian t->tm_sec += val; 38059022Sbrian if (t->tm_sec > 59) 38159022Sbrian t->tm_sec -= 60; 38259022Sbrian } 38344598Sbrian break; 38444598Sbrian 38544598Sbrian case '-': 38659022Sbrian if (val) { 38759175Sbrian if (!adjmin(t, '-', val / 60, 0)) 38844598Sbrian return 0; 38959022Sbrian val %= 60; 39059022Sbrian if (val > t->tm_sec) { 39159175Sbrian if (!adjmin(t, '-', 1, 0)) 39259022Sbrian return 0; 39359022Sbrian val -= 60; 39459022Sbrian } 39559022Sbrian t->tm_sec -= val; 39644598Sbrian } 39744598Sbrian break; 39844598Sbrian 39944598Sbrian default: 40044598Sbrian if (val > 59) 40144598Sbrian return 0; 40244598Sbrian t->tm_sec = val; 40344598Sbrian } 40444598Sbrian 40559175Sbrian return !mk || domktime(t, type) != -1; 40644598Sbrian} 40744598Sbrian 40827874Sbrianconst struct vary * 40927874Sbrianvary_apply(const struct vary *v, struct tm *t) 41027874Sbrian{ 41128025Sbrian char type; 41228025Sbrian char which; 41328025Sbrian char *arg; 41428025Sbrian int len; 41528025Sbrian int val; 41628025Sbrian 41727874Sbrian for (; v; v = v->next) { 41828025Sbrian type = *v->arg; 41928025Sbrian arg = v->arg; 42028025Sbrian if (type == '+' || type == '-') 42128025Sbrian arg++; 42228025Sbrian else 42328025Sbrian type = '\0'; 42428025Sbrian len = strlen(arg); 42528025Sbrian if (len < 2) 42628025Sbrian return v; 42728025Sbrian 42859175Sbrian if (type == '\0') 42959175Sbrian t->tm_isdst = -1; 43059175Sbrian 43128025Sbrian if (strspn(arg, digits) != len-1) { 43228025Sbrian val = trans(trans_wday, arg); 43328025Sbrian if (val != -1) { 43459175Sbrian if (!adjwday(t, type, val, 1, 1)) 43528025Sbrian return v; 43628025Sbrian } else { 43728025Sbrian val = trans(trans_mon, arg); 43828025Sbrian if (val != -1) { 43959175Sbrian if (!adjmon(t, type, val, 1, 1)) 44028025Sbrian return v; 44128025Sbrian } else 44227874Sbrian return v; 44328025Sbrian } 44428025Sbrian } else { 44528025Sbrian val = atoi(arg); 44628025Sbrian which = arg[len-1]; 44728025Sbrian 44828025Sbrian switch (which) { 44944598Sbrian case 'S': 45059175Sbrian if (!adjsec(t, type, val, 1)) 45144598Sbrian return v; 45244598Sbrian break; 45328025Sbrian case 'M': 45459175Sbrian if (!adjmin(t, type, val, 1)) 45528025Sbrian return v; 45628025Sbrian break; 45728025Sbrian case 'H': 45859175Sbrian if (!adjhour(t, type, val, 1)) 45928025Sbrian return v; 46028025Sbrian break; 46128025Sbrian case 'd': 46259175Sbrian t->tm_isdst = -1; 46359175Sbrian if (!adjday(t, type, val, 1)) 46428025Sbrian return v; 46528025Sbrian break; 46628025Sbrian case 'w': 46759175Sbrian t->tm_isdst = -1; 46859175Sbrian if (!adjwday(t, type, val, 0, 1)) 46928025Sbrian return v; 47028025Sbrian break; 47128025Sbrian case 'm': 47259175Sbrian t->tm_isdst = -1; 47359175Sbrian if (!adjmon(t, type, val, 0, 1)) 47428025Sbrian return v; 47528025Sbrian break; 47628025Sbrian case 'y': 47759175Sbrian t->tm_isdst = -1; 47859175Sbrian if (!adjyear(t, type, val, 1)) 47928025Sbrian return v; 48028025Sbrian break; 48128025Sbrian default: 48227874Sbrian return v; 48328025Sbrian } 48427874Sbrian } 48527874Sbrian } 48627874Sbrian return 0; 48727874Sbrian} 48827874Sbrian 48927874Sbrianvoid 49027874Sbrianvary_destroy(struct vary *v) 49127874Sbrian{ 49227874Sbrian struct vary *n; 49327874Sbrian 49427874Sbrian while (v) { 49527874Sbrian n = v->next; 49627874Sbrian free(v); 49727874Sbrian v = n; 49827874Sbrian } 49927874Sbrian} 500