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