vary.c revision 50471
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 50471 1999-08-27 23:15:48Z peter $"; 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" }, 4427874Sbrian { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" }, 4527874Sbrian { 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"; 5627874Sbrian 5727874Sbrianstatic int 5827874Sbriantrans(const struct trans t[], const char *arg) 5927874Sbrian{ 6027874Sbrian int f; 6127874Sbrian 6227874Sbrian for (f = 0; t[f].val != -1; f++) 6328025Sbrian if (!strncasecmp(t[f].str, arg, 3) || 6428025Sbrian !strncasecmp(t[f].str, arg, strlen(t[f].str))) 6527874Sbrian return t[f].val; 6627874Sbrian 6727874Sbrian return -1; 6827874Sbrian} 6927874Sbrian 7027874Sbrianstruct vary * 7128025Sbrianvary_append(struct vary *v, char *arg) 7227874Sbrian{ 7327874Sbrian struct vary *result, **nextp; 7427874Sbrian 7527874Sbrian if (v) { 7627874Sbrian result = v; 7727874Sbrian while (v->next) 7827874Sbrian v = v->next; 7927874Sbrian nextp = &v->next; 8027874Sbrian } else 8127874Sbrian nextp = &result; 8227874Sbrian 8327874Sbrian *nextp = (struct vary *)malloc(sizeof(struct vary)); 8427874Sbrian (*nextp)->arg = arg; 8527874Sbrian (*nextp)->next = NULL; 8627874Sbrian return result; 8727874Sbrian} 8827874Sbrian 8927874Sbrianstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 9027874Sbrian 9127874Sbrianstatic int 9227874Sbriandaysinmonth(const struct tm *t) 9327874Sbrian{ 9427874Sbrian int year; 9527874Sbrian 9627874Sbrian year = t->tm_year + 1900; 9727874Sbrian 9827874Sbrian if (t->tm_mon == 1) 9927874Sbrian if (!(year % 400)) 10027874Sbrian return 29; 10127874Sbrian else if (!(year % 100)) 10227874Sbrian return 28; 10327874Sbrian else if (!(year % 4)) 10427874Sbrian return 29; 10527874Sbrian else 10627874Sbrian return 28; 10727874Sbrian else if (t->tm_mon >= 0 && t->tm_mon < 12) 10827874Sbrian return mdays[t->tm_mon]; 10927874Sbrian 11027874Sbrian return 0; 11127874Sbrian} 11227874Sbrian 11327874Sbrian 11427874Sbrianstatic int 11527874Sbrianadjyear(struct tm *t, char type, int val) 11627874Sbrian{ 11727874Sbrian switch (type) { 11827874Sbrian case '+': 11927874Sbrian t->tm_year += val; 12027874Sbrian break; 12127874Sbrian case '-': 12227874Sbrian t->tm_year -= val; 12327874Sbrian break; 12427874Sbrian default: 12527874Sbrian t->tm_year = val; 12627874Sbrian if (t->tm_year < 69) 12727874Sbrian t->tm_year += 100; /* as per date.c */ 12827874Sbrian else if (t->tm_year > 1900) 12927874Sbrian t->tm_year -= 1900; /* struct tm holds years since 1900 */ 13027874Sbrian break; 13127874Sbrian } 13227874Sbrian return mktime(t) != -1; 13327874Sbrian} 13427874Sbrian 13527874Sbrianstatic int 13627874Sbrianadjmon(struct tm *t, char type, int val, int istext) 13727874Sbrian{ 13827874Sbrian if (val < 0) 13927874Sbrian return 0; 14027874Sbrian 14127874Sbrian switch (type) { 14227874Sbrian case '+': 14346073Simp if (istext) { 14427874Sbrian if (val <= t->tm_mon) 14527874Sbrian val += 11 - t->tm_mon; /* early next year */ 14627874Sbrian else 14727874Sbrian val -= t->tm_mon + 1; /* later this year */ 14846073Simp } 14927874Sbrian if (!adjyear(t, '+', (t->tm_mon + val) / 12)) 15027874Sbrian return 0; 15127874Sbrian val %= 12; 15227874Sbrian t->tm_mon += val; 15327874Sbrian if (t->tm_mon > 11) 15427874Sbrian t->tm_mon -= 12; 15527874Sbrian break; 15627874Sbrian 15727874Sbrian case '-': 15846073Simp if (istext) { 15927874Sbrian if (val-1 > t->tm_mon) 16027874Sbrian val = 13 - val + t->tm_mon; /* later last year */ 16127874Sbrian else 16227874Sbrian val = t->tm_mon - val + 1; /* early this year */ 16346073Simp } 16427874Sbrian if (!adjyear(t, '-', val / 12)) 16527874Sbrian return 0; 16627874Sbrian val %= 12; 16727874Sbrian if (val > t->tm_mon) { 16827874Sbrian if (!adjyear(t, '-', 1)) 16927874Sbrian return 0; 17027874Sbrian val -= 12; 17127874Sbrian } 17227874Sbrian t->tm_mon -= val; 17327874Sbrian break; 17427874Sbrian 17527874Sbrian default: 17627874Sbrian if (val > 12 || val < 1) 17727874Sbrian return 0; 17827874Sbrian t->tm_mon = --val; 17927874Sbrian } 18027874Sbrian 18127874Sbrian return mktime(t) != -1; 18227874Sbrian} 18327874Sbrian 18427874Sbrianstatic int 18527874Sbrianadjday(struct tm *t, char type, int val) 18627874Sbrian{ 18727874Sbrian int mdays; 18827874Sbrian switch (type) { 18927874Sbrian case '+': 19027874Sbrian while (val) { 19127874Sbrian mdays = daysinmonth(t); 19227874Sbrian if (val > mdays - t->tm_mday) { 19327874Sbrian val -= mdays - t->tm_mday + 1; 19427874Sbrian t->tm_mday = 1; 19527874Sbrian if (!adjmon(t, '+', 1, 0)) 19627874Sbrian return 0; 19727874Sbrian } else { 19827874Sbrian t->tm_mday += val; 19927874Sbrian val = 0; 20027874Sbrian } 20127874Sbrian } 20227874Sbrian break; 20327874Sbrian case '-': 20427874Sbrian while (val) 20527874Sbrian if (val >= t->tm_mday) { 20627874Sbrian val -= t->tm_mday; 20727874Sbrian t->tm_mday = 1; 20827874Sbrian if (!adjmon(t, '-', 1, 0)) 20927874Sbrian return 0; 21027874Sbrian t->tm_mday = daysinmonth(t); 21127874Sbrian } else { 21227874Sbrian t->tm_mday -= val; 21327874Sbrian val = 0; 21427874Sbrian } 21527874Sbrian break; 21627874Sbrian default: 21727874Sbrian if (val > 0 && val <= daysinmonth(t)) 21827874Sbrian t->tm_mday = val; 21927874Sbrian else 22027874Sbrian return 0; 22127874Sbrian break; 22227874Sbrian } 22327874Sbrian 22427874Sbrian return mktime(t) != -1; 22527874Sbrian} 22627874Sbrian 22727874Sbrianstatic int 22828025Sbrianadjwday(struct tm *t, char type, int val, int istext) 22927874Sbrian{ 23027874Sbrian if (val < 0) 23127874Sbrian return 0; 23227874Sbrian 23328025Sbrian switch (type) { 23427874Sbrian case '+': 23527874Sbrian if (istext) 23627874Sbrian if (val < t->tm_wday) 23727874Sbrian val = 7 - t->tm_wday + val; /* early next week */ 23827874Sbrian else 23927874Sbrian val -= t->tm_wday; /* later this week */ 24027874Sbrian else 24127874Sbrian val *= 7; /* "-W +5" == "5 weeks in the future" */ 24227874Sbrian return adjday(t, '+', val); 24327874Sbrian case '-': 24427874Sbrian if (istext) 24527874Sbrian if (val > t->tm_wday) 24627874Sbrian val = 7 - val + t->tm_wday; /* later last week */ 24727874Sbrian else 24827874Sbrian val = t->tm_wday - val; /* early this week */ 24927874Sbrian else 25027874Sbrian val *= 7; /* "-W -5" == "5 weeks ago" */ 25127874Sbrian return adjday(t, '-', val); 25227874Sbrian default: 25327874Sbrian if (val < t->tm_wday) 25427874Sbrian return adjday(t, '-', t->tm_wday - val); 25527874Sbrian else if (val > 6) 25627874Sbrian return 0; 25727874Sbrian else if (val > t->tm_wday) 25827874Sbrian return adjday(t, '+', val - t->tm_wday); 25927874Sbrian } 26027874Sbrian return 1; 26127874Sbrian} 26227874Sbrian 26327874Sbrianstatic int 26428025Sbrianadjhour(struct tm *t, char type, int val) 26527874Sbrian{ 26628025Sbrian if (val < 0) 26728025Sbrian return 0; 26828025Sbrian 26928025Sbrian switch (type) { 27027874Sbrian case '+': 27128025Sbrian if (!adjday(t, '+', (t->tm_hour + val) / 24)) 27228025Sbrian return 0; 27328025Sbrian val %= 24; 27428025Sbrian t->tm_hour += val; 27528025Sbrian if (t->tm_hour > 23) 27628025Sbrian t->tm_hour -= 24; 27728025Sbrian break; 27828025Sbrian 27927874Sbrian case '-': 28028025Sbrian if (!adjday(t, '-', val / 24)) 28128025Sbrian return 0; 28228025Sbrian val %= 24; 28328025Sbrian if (val > t->tm_hour) { 28428025Sbrian if (!adjday(t, '-', 1)) 28528025Sbrian return 0; 28628025Sbrian val -= 24; 28728025Sbrian } 28828025Sbrian t->tm_hour -= val; 28928025Sbrian break; 29028025Sbrian 29127874Sbrian default: 29228025Sbrian if (val > 23) 29328025Sbrian return 0; 29428025Sbrian t->tm_hour = val; 29527874Sbrian } 29628025Sbrian 29728025Sbrian return mktime(t) != -1; 29827874Sbrian} 29927874Sbrian 30028025Sbrianstatic int 30128025Sbrianadjmin(struct tm *t, char type, int val) 30228025Sbrian{ 30328025Sbrian if (val < 0) 30428025Sbrian return 0; 30528025Sbrian 30628025Sbrian switch (type) { 30728025Sbrian case '+': 30828025Sbrian if (!adjhour(t, '+', (t->tm_min + val) / 60)) 30928025Sbrian return 0; 31028025Sbrian val %= 60; 31128025Sbrian t->tm_min += val; 31228025Sbrian if (t->tm_min > 59) 31328025Sbrian t->tm_min -= 60; 31428025Sbrian break; 31528025Sbrian 31628025Sbrian case '-': 31728025Sbrian if (!adjhour(t, '-', val / 60)) 31828025Sbrian return 0; 31928025Sbrian val %= 60; 32028025Sbrian if (val > t->tm_min) { 32128025Sbrian if (!adjhour(t, '-', 1)) 32228025Sbrian return 0; 32328025Sbrian val -= 60; 32428025Sbrian } 32528025Sbrian t->tm_min -= val; 32628025Sbrian break; 32728025Sbrian 32828025Sbrian default: 32928025Sbrian if (val > 59) 33028025Sbrian return 0; 33128025Sbrian t->tm_min = val; 33228025Sbrian } 33328025Sbrian 33428025Sbrian return mktime(t) != -1; 33528025Sbrian} 33628025Sbrian 33744598Sbrianstatic int 33844598Sbrianadjsec(struct tm *t, char type, int val) 33944598Sbrian{ 34044598Sbrian if (val < 0) 34144598Sbrian return 0; 34244598Sbrian 34344598Sbrian switch (type) { 34444598Sbrian case '+': 34544598Sbrian if (!adjmin(t, '+', (t->tm_sec + val) / 60)) 34644598Sbrian return 0; 34744598Sbrian val %= 60; 34844598Sbrian t->tm_sec += val; 34944598Sbrian if (t->tm_sec > 59) 35044598Sbrian t->tm_sec -= 60; 35144598Sbrian break; 35244598Sbrian 35344598Sbrian case '-': 35444598Sbrian if (!adjmin(t, '-', val / 60)) 35544598Sbrian return 0; 35644598Sbrian val %= 60; 35744598Sbrian if (val > t->tm_sec) { 35844598Sbrian if (!adjmin(t, '-', 1)) 35944598Sbrian return 0; 36044598Sbrian val -= 60; 36144598Sbrian } 36244598Sbrian t->tm_sec -= val; 36344598Sbrian break; 36444598Sbrian 36544598Sbrian default: 36644598Sbrian if (val > 59) 36744598Sbrian return 0; 36844598Sbrian t->tm_sec = val; 36944598Sbrian } 37044598Sbrian 37144598Sbrian return mktime(t) != -1; 37244598Sbrian} 37344598Sbrian 37427874Sbrianconst struct vary * 37527874Sbrianvary_apply(const struct vary *v, struct tm *t) 37627874Sbrian{ 37728025Sbrian char type; 37828025Sbrian char which; 37928025Sbrian char *arg; 38028025Sbrian int len; 38128025Sbrian int val; 38228025Sbrian 38327874Sbrian for (; v; v = v->next) { 38428025Sbrian type = *v->arg; 38528025Sbrian arg = v->arg; 38628025Sbrian if (type == '+' || type == '-') 38728025Sbrian arg++; 38828025Sbrian else 38928025Sbrian type = '\0'; 39028025Sbrian len = strlen(arg); 39128025Sbrian if (len < 2) 39228025Sbrian return v; 39328025Sbrian 39428025Sbrian if (strspn(arg, digits) != len-1) { 39528025Sbrian val = trans(trans_wday, arg); 39628025Sbrian if (val != -1) { 39728025Sbrian if (!adjwday(t, type, val, 1)) 39828025Sbrian return v; 39928025Sbrian } else { 40028025Sbrian val = trans(trans_mon, arg); 40128025Sbrian if (val != -1) { 40228025Sbrian if (!adjmon(t, type, val, 1)) 40328025Sbrian return v; 40428025Sbrian } else 40527874Sbrian return v; 40628025Sbrian } 40728025Sbrian } else { 40828025Sbrian val = atoi(arg); 40928025Sbrian which = arg[len-1]; 41028025Sbrian 41128025Sbrian switch (which) { 41244598Sbrian case 'S': 41344598Sbrian if (!adjsec(t, type, val)) 41444598Sbrian return v; 41544598Sbrian break; 41628025Sbrian case 'M': 41728025Sbrian if (!adjmin(t, type, val)) 41828025Sbrian return v; 41928025Sbrian break; 42028025Sbrian case 'H': 42128025Sbrian if (!adjhour(t, type, val)) 42228025Sbrian return v; 42328025Sbrian break; 42428025Sbrian case 'd': 42528025Sbrian if (!adjday(t, type, val)) 42628025Sbrian return v; 42728025Sbrian break; 42828025Sbrian case 'w': 42928025Sbrian if (!adjwday(t, type, val, 0)) 43028025Sbrian return v; 43128025Sbrian break; 43228025Sbrian case 'm': 43328025Sbrian if (!adjmon(t, type, val, 0)) 43428025Sbrian return v; 43528025Sbrian break; 43628025Sbrian case 'y': 43728025Sbrian if (!adjyear(t, type, val)) 43828025Sbrian return v; 43928025Sbrian break; 44028025Sbrian default: 44127874Sbrian return v; 44228025Sbrian } 44327874Sbrian } 44427874Sbrian } 44527874Sbrian return 0; 44627874Sbrian} 44727874Sbrian 44827874Sbrianvoid 44927874Sbrianvary_destroy(struct vary *v) 45027874Sbrian{ 45127874Sbrian struct vary *n; 45227874Sbrian 45327874Sbrian while (v) { 45427874Sbrian n = v->next; 45527874Sbrian free(v); 45627874Sbrian v = n; 45727874Sbrian } 45827874Sbrian} 459