vary.c revision 59022
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 59022 2000-04-05 01:59:36Z 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";
5659022Sbrianstatic int adjhour(struct tm *, char, int);
5727874Sbrian
5827874Sbrianstatic int
5959022Sbriandomktime(struct tm *t, char type)
6059022Sbrian{
6159022Sbrian  int ret;
6259022Sbrian
6359022Sbrian  /*
6459022Sbrian   * Don't let mktime have a specific tm_isdst value.  If we do,
6559022Sbrian   * it gets confused when we ``vary'' in and out of Summer time
6659022Sbrian   */
6759022Sbrian  t->tm_isdst = -1;
6859022Sbrian  if ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
6959022Sbrian    /*
7059022Sbrian     * If mktime() fails, adjust by an hour.  If we're actually
7159022Sbrian     * bridging a savings of several hours, we'll recurse several
7259022Sbrian     * times.
7359022Sbrian     */
7459022Sbrian    ret = adjhour(t, type == '-' ? type : '+', 1);
7559022Sbrian
7659022Sbrian  return ret;
7759022Sbrian}
7859022Sbrian
7959022Sbrianstatic int
8027874Sbriantrans(const struct trans t[], const char *arg)
8127874Sbrian{
8227874Sbrian  int f;
8327874Sbrian
8427874Sbrian  for (f = 0; t[f].val != -1; f++)
8528025Sbrian    if (!strncasecmp(t[f].str, arg, 3) ||
8628025Sbrian        !strncasecmp(t[f].str, arg, strlen(t[f].str)))
8727874Sbrian      return t[f].val;
8827874Sbrian
8927874Sbrian  return -1;
9027874Sbrian}
9127874Sbrian
9227874Sbrianstruct vary *
9328025Sbrianvary_append(struct vary *v, char *arg)
9427874Sbrian{
9527874Sbrian  struct vary *result, **nextp;
9627874Sbrian
9727874Sbrian  if (v) {
9827874Sbrian    result = v;
9927874Sbrian    while (v->next)
10027874Sbrian      v = v->next;
10127874Sbrian    nextp = &v->next;
10227874Sbrian  } else
10327874Sbrian    nextp = &result;
10427874Sbrian
10527874Sbrian  *nextp = (struct vary *)malloc(sizeof(struct vary));
10627874Sbrian  (*nextp)->arg = arg;
10727874Sbrian  (*nextp)->next = NULL;
10827874Sbrian  return result;
10927874Sbrian}
11027874Sbrian
11127874Sbrianstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
11227874Sbrian
11327874Sbrianstatic int
11427874Sbriandaysinmonth(const struct tm *t)
11527874Sbrian{
11627874Sbrian  int year;
11727874Sbrian
11827874Sbrian  year = t->tm_year + 1900;
11927874Sbrian
12027874Sbrian  if (t->tm_mon == 1)
12127874Sbrian    if (!(year % 400))
12227874Sbrian      return 29;
12327874Sbrian    else if (!(year % 100))
12427874Sbrian      return 28;
12527874Sbrian    else if (!(year % 4))
12627874Sbrian      return 29;
12727874Sbrian    else
12827874Sbrian      return 28;
12927874Sbrian  else if (t->tm_mon >= 0 && t->tm_mon < 12)
13027874Sbrian    return mdays[t->tm_mon];
13127874Sbrian
13227874Sbrian  return 0;
13327874Sbrian}
13427874Sbrian
13527874Sbrian
13627874Sbrianstatic int
13727874Sbrianadjyear(struct tm *t, char type, int val)
13827874Sbrian{
13927874Sbrian  switch (type) {
14027874Sbrian    case '+':
14127874Sbrian      t->tm_year += val;
14227874Sbrian      break;
14327874Sbrian    case '-':
14427874Sbrian      t->tm_year -= val;
14527874Sbrian      break;
14627874Sbrian    default:
14727874Sbrian      t->tm_year = val;
14827874Sbrian      if (t->tm_year < 69)
14927874Sbrian      	t->tm_year += 100;		/* as per date.c */
15027874Sbrian      else if (t->tm_year > 1900)
15127874Sbrian        t->tm_year -= 1900;             /* struct tm holds years since 1900 */
15227874Sbrian      break;
15327874Sbrian  }
15459022Sbrian  return domktime(t, type) != -1;
15527874Sbrian}
15627874Sbrian
15727874Sbrianstatic int
15827874Sbrianadjmon(struct tm *t, char type, int val, int istext)
15927874Sbrian{
16027874Sbrian  if (val < 0)
16127874Sbrian    return 0;
16227874Sbrian
16327874Sbrian  switch (type) {
16427874Sbrian    case '+':
16546073Simp      if (istext) {
16627874Sbrian        if (val <= t->tm_mon)
16727874Sbrian          val += 11 - t->tm_mon;	/* early next year */
16827874Sbrian        else
16927874Sbrian          val -= t->tm_mon + 1;		/* later this year */
17046073Simp      }
17159022Sbrian      if (val) {
17259022Sbrian        if (!adjyear(t, '+', (t->tm_mon + val) / 12))
17359022Sbrian          return 0;
17459022Sbrian        val %= 12;
17559022Sbrian        t->tm_mon += val;
17659022Sbrian        if (t->tm_mon > 11)
17759022Sbrian          t->tm_mon -= 12;
17859022Sbrian      }
17927874Sbrian      break;
18027874Sbrian
18127874Sbrian    case '-':
18246073Simp      if (istext) {
18327874Sbrian        if (val-1 > t->tm_mon)
18427874Sbrian          val = 13 - val + t->tm_mon;	/* later last year */
18527874Sbrian        else
18627874Sbrian          val = t->tm_mon - val + 1;	/* early this year */
18746073Simp      }
18859022Sbrian      if (val) {
18959022Sbrian        if (!adjyear(t, '-', val / 12))
19027874Sbrian          return 0;
19159022Sbrian        val %= 12;
19259022Sbrian        if (val > t->tm_mon) {
19359022Sbrian          if (!adjyear(t, '-', 1))
19459022Sbrian            return 0;
19559022Sbrian          val -= 12;
19659022Sbrian        }
19759022Sbrian        t->tm_mon -= val;
19827874Sbrian      }
19927874Sbrian      break;
20027874Sbrian
20127874Sbrian    default:
20227874Sbrian      if (val > 12 || val < 1)
20327874Sbrian        return 0;
20427874Sbrian      t->tm_mon = --val;
20527874Sbrian  }
20627874Sbrian
20759022Sbrian  return domktime(t, type) != -1;
20827874Sbrian}
20927874Sbrian
21027874Sbrianstatic int
21127874Sbrianadjday(struct tm *t, char type, int val)
21227874Sbrian{
21327874Sbrian  int mdays;
21459022Sbrian
21527874Sbrian  switch (type) {
21627874Sbrian    case '+':
21727874Sbrian      while (val) {
21827874Sbrian        mdays = daysinmonth(t);
21927874Sbrian        if (val > mdays - t->tm_mday) {
22027874Sbrian          val -= mdays - t->tm_mday + 1;
22127874Sbrian          t->tm_mday = 1;
22227874Sbrian          if (!adjmon(t, '+', 1, 0))
22327874Sbrian            return 0;
22427874Sbrian        } else {
22527874Sbrian          t->tm_mday += val;
22627874Sbrian          val = 0;
22727874Sbrian        }
22827874Sbrian      }
22927874Sbrian      break;
23027874Sbrian    case '-':
23127874Sbrian      while (val)
23227874Sbrian        if (val >= t->tm_mday) {
23327874Sbrian          val -= t->tm_mday;
23427874Sbrian          t->tm_mday = 1;
23527874Sbrian          if (!adjmon(t, '-', 1, 0))
23627874Sbrian            return 0;
23727874Sbrian          t->tm_mday = daysinmonth(t);
23827874Sbrian        } else {
23927874Sbrian          t->tm_mday -= val;
24027874Sbrian          val = 0;
24127874Sbrian        }
24227874Sbrian      break;
24327874Sbrian    default:
24427874Sbrian      if (val > 0 && val <= daysinmonth(t))
24527874Sbrian        t->tm_mday = val;
24627874Sbrian      else
24727874Sbrian        return 0;
24827874Sbrian      break;
24927874Sbrian  }
25027874Sbrian
25159022Sbrian  return domktime(t, type) != -1;
25227874Sbrian}
25327874Sbrian
25427874Sbrianstatic int
25528025Sbrianadjwday(struct tm *t, char type, int val, int istext)
25627874Sbrian{
25727874Sbrian  if (val < 0)
25827874Sbrian    return 0;
25927874Sbrian
26028025Sbrian  switch (type) {
26127874Sbrian    case '+':
26227874Sbrian      if (istext)
26327874Sbrian        if (val < t->tm_wday)
26427874Sbrian          val = 7 - t->tm_wday + val;  /* early next week */
26527874Sbrian        else
26627874Sbrian          val -= t->tm_wday;           /* later this week */
26727874Sbrian      else
26859022Sbrian        val *= 7;                      /* "-v+5w" == "5 weeks in the future" */
26959022Sbrian      return !val || adjday(t, '+', val);
27027874Sbrian    case '-':
27159022Sbrian      if (istext) {
27227874Sbrian        if (val > t->tm_wday)
27327874Sbrian          val = 7 - val + t->tm_wday;  /* later last week */
27427874Sbrian        else
27527874Sbrian          val = t->tm_wday - val;      /* early this week */
27659022Sbrian      } else
27759022Sbrian        val *= 7;                      /* "-v-5w" == "5 weeks ago" */
27859022Sbrian      return !val || adjday(t, '-', val);
27927874Sbrian    default:
28027874Sbrian      if (val < t->tm_wday)
28127874Sbrian        return adjday(t, '-', t->tm_wday - val);
28227874Sbrian      else if (val > 6)
28327874Sbrian        return 0;
28427874Sbrian      else if (val > t->tm_wday)
28527874Sbrian        return adjday(t, '+', val - t->tm_wday);
28627874Sbrian  }
28727874Sbrian  return 1;
28827874Sbrian}
28927874Sbrian
29027874Sbrianstatic int
29128025Sbrianadjhour(struct tm *t, char type, int val)
29227874Sbrian{
29359022Sbrian  /*
29459022Sbrian   * We've got to be careful here in case
29559022Sbrian   *   domktime() calls
29659022Sbrian   *   adjhour() calls
29759022Sbrian   *   adjday() calls
29859022Sbrian   *   domktime() calls
29959022Sbrian   *   adjhour() and recurses forever.  It's vital that we've adjusted our
30059022Sbrian   *   number of hours before calling adjday().
30159022Sbrian   */
30259022Sbrian
30328025Sbrian  if (val < 0)
30428025Sbrian    return 0;
30528025Sbrian
30628025Sbrian  switch (type) {
30727874Sbrian    case '+':
30859022Sbrian      if (val) {
30959022Sbrian        int days;
31059022Sbrian
31159022Sbrian        days = (t->tm_hour + val) / 24;
31259022Sbrian        val %= 24;
31359022Sbrian        t->tm_hour += val;
31459022Sbrian        t->tm_hour %= 24;
31559022Sbrian        if (!adjday(t, '+', days))
31659022Sbrian          return 0;
31759022Sbrian      }
31828025Sbrian      break;
31928025Sbrian
32027874Sbrian    case '-':
32159022Sbrian      if (val) {
32259022Sbrian        int days;
32359022Sbrian
32459022Sbrian        days = val / 24;
32559022Sbrian        val %= 24;
32659022Sbrian        if (val > t->tm_hour) {
32759022Sbrian          days++;
32859022Sbrian          val -= 24;
32959022Sbrian        }
33059022Sbrian        t->tm_hour -= val;
33159022Sbrian        if (!adjday(t, '-', val / 24))
33228025Sbrian          return 0;
33328025Sbrian      }
33428025Sbrian      break;
33528025Sbrian
33627874Sbrian    default:
33728025Sbrian      if (val > 23)
33828025Sbrian        return 0;
33928025Sbrian      t->tm_hour = val;
34027874Sbrian  }
34128025Sbrian
34259022Sbrian  return domktime(t, type) != -1;
34327874Sbrian}
34427874Sbrian
34528025Sbrianstatic int
34628025Sbrianadjmin(struct tm *t, char type, int val)
34728025Sbrian{
34828025Sbrian  if (val < 0)
34928025Sbrian    return 0;
35028025Sbrian
35128025Sbrian  switch (type) {
35228025Sbrian    case '+':
35359022Sbrian      if (val) {
35459022Sbrian        if (!adjhour(t, '+', (t->tm_min + val) / 60))
35559022Sbrian          return 0;
35659022Sbrian        val %= 60;
35759022Sbrian        t->tm_min += val;
35859022Sbrian        if (t->tm_min > 59)
35959022Sbrian          t->tm_min -= 60;
36059022Sbrian      }
36128025Sbrian      break;
36228025Sbrian
36328025Sbrian    case '-':
36459022Sbrian      if (val) {
36559022Sbrian        if (!adjhour(t, '-', val / 60))
36628025Sbrian          return 0;
36759022Sbrian        val %= 60;
36859022Sbrian        if (val > t->tm_min) {
36959022Sbrian          if (!adjhour(t, '-', 1))
37059022Sbrian            return 0;
37159022Sbrian          val -= 60;
37259022Sbrian        }
37359022Sbrian        t->tm_min -= val;
37428025Sbrian      }
37528025Sbrian      break;
37628025Sbrian
37728025Sbrian    default:
37828025Sbrian      if (val > 59)
37928025Sbrian        return 0;
38028025Sbrian      t->tm_min = val;
38128025Sbrian  }
38228025Sbrian
38359022Sbrian  return domktime(t, type) != -1;
38428025Sbrian}
38528025Sbrian
38644598Sbrianstatic int
38744598Sbrianadjsec(struct tm *t, char type, int val)
38844598Sbrian{
38944598Sbrian  if (val < 0)
39044598Sbrian    return 0;
39144598Sbrian
39244598Sbrian  switch (type) {
39344598Sbrian    case '+':
39459022Sbrian      if (val) {
39559022Sbrian        if (!adjmin(t, '+', (t->tm_sec + val) / 60))
39659022Sbrian          return 0;
39759022Sbrian        val %= 60;
39859022Sbrian        t->tm_sec += val;
39959022Sbrian        if (t->tm_sec > 59)
40059022Sbrian          t->tm_sec -= 60;
40159022Sbrian      }
40244598Sbrian      break;
40344598Sbrian
40444598Sbrian    case '-':
40559022Sbrian      if (val) {
40659022Sbrian        if (!adjmin(t, '-', val / 60))
40744598Sbrian          return 0;
40859022Sbrian        val %= 60;
40959022Sbrian        if (val > t->tm_sec) {
41059022Sbrian          if (!adjmin(t, '-', 1))
41159022Sbrian            return 0;
41259022Sbrian          val -= 60;
41359022Sbrian        }
41459022Sbrian        t->tm_sec -= val;
41544598Sbrian      }
41644598Sbrian      break;
41744598Sbrian
41844598Sbrian    default:
41944598Sbrian      if (val > 59)
42044598Sbrian        return 0;
42144598Sbrian      t->tm_sec = val;
42244598Sbrian  }
42344598Sbrian
42459022Sbrian  return domktime(t, type) != -1;
42544598Sbrian}
42644598Sbrian
42727874Sbrianconst struct vary *
42827874Sbrianvary_apply(const struct vary *v, struct tm *t)
42927874Sbrian{
43028025Sbrian  char type;
43128025Sbrian  char which;
43228025Sbrian  char *arg;
43328025Sbrian  int len;
43428025Sbrian  int val;
43528025Sbrian
43627874Sbrian  for (; v; v = v->next) {
43728025Sbrian    type = *v->arg;
43828025Sbrian    arg = v->arg;
43928025Sbrian    if (type == '+' || type == '-')
44028025Sbrian      arg++;
44128025Sbrian    else
44228025Sbrian      type = '\0';
44328025Sbrian    len = strlen(arg);
44428025Sbrian    if (len < 2)
44528025Sbrian      return v;
44628025Sbrian
44728025Sbrian    if (strspn(arg, digits) != len-1) {
44828025Sbrian      val = trans(trans_wday, arg);
44928025Sbrian      if (val != -1) {
45028025Sbrian          if (!adjwday(t, type, val, 1))
45128025Sbrian            return v;
45228025Sbrian      } else {
45328025Sbrian        val = trans(trans_mon, arg);
45428025Sbrian        if (val != -1) {
45528025Sbrian          if (!adjmon(t, type, val, 1))
45628025Sbrian            return v;
45728025Sbrian        } else
45827874Sbrian          return v;
45928025Sbrian      }
46028025Sbrian    } else {
46128025Sbrian      val = atoi(arg);
46228025Sbrian      which = arg[len-1];
46328025Sbrian
46428025Sbrian      switch (which) {
46544598Sbrian        case 'S':
46644598Sbrian          if (!adjsec(t, type, val))
46744598Sbrian            return v;
46844598Sbrian          break;
46928025Sbrian        case 'M':
47028025Sbrian          if (!adjmin(t, type, val))
47128025Sbrian            return v;
47228025Sbrian          break;
47328025Sbrian        case 'H':
47428025Sbrian          if (!adjhour(t, type, val))
47528025Sbrian            return v;
47628025Sbrian          break;
47728025Sbrian        case 'd':
47828025Sbrian          if (!adjday(t, type, val))
47928025Sbrian            return v;
48028025Sbrian          break;
48128025Sbrian        case 'w':
48228025Sbrian          if (!adjwday(t, type, val, 0))
48328025Sbrian            return v;
48428025Sbrian          break;
48528025Sbrian        case 'm':
48628025Sbrian          if (!adjmon(t, type, val, 0))
48728025Sbrian            return v;
48828025Sbrian          break;
48928025Sbrian        case 'y':
49028025Sbrian          if (!adjyear(t, type, val))
49128025Sbrian            return v;
49228025Sbrian          break;
49328025Sbrian        default:
49427874Sbrian          return v;
49528025Sbrian      }
49627874Sbrian    }
49727874Sbrian  }
49827874Sbrian  return 0;
49927874Sbrian}
50027874Sbrian
50127874Sbrianvoid
50227874Sbrianvary_destroy(struct vary *v)
50327874Sbrian{
50427874Sbrian  struct vary *n;
50527874Sbrian
50627874Sbrian  while (v) {
50727874Sbrian    n = v->next;
50827874Sbrian    free(v);
50927874Sbrian    v = n;
51027874Sbrian  }
51127874Sbrian}
512