vary.c revision 60836
169793Sobrien/*-
269793Sobrien * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
315702Sphk * All rights reserved.
415702Sphk *
515702Sphk * Redistribution and use in source and binary forms, with or without
615702Sphk * modification, are permitted provided that the following conditions
715702Sphk * are met:
815773Sphk * 1. Redistributions of source code must retain the above copyright
915773Sphk *    notice, this list of conditions and the following disclaimer.
1015702Sphk * 2. Redistributions in binary form must reproduce the above copyright
1119813Sphk *    notice, this list of conditions and the following disclaimer in the
1215702Sphk *    documentation and/or other materials provided with the distribution.
1315702Sphk *
1415702Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1515702Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1615702Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17118583Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1815702Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1915702Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2015702Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2115702Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2215702Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2315702Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2415702Sphk * SUCH DAMAGE.
2515702Sphk */
2615702Sphk
2715702Sphk#ifndef lint
2815702Sphkstatic const char rcsid[] =
2915702Sphk  "$FreeBSD: head/bin/date/vary.c 60836 2000-05-23 23:58:18Z brian $";
3015702Sphk#endif /* not lint */
3115702Sphk
3215702Sphk#include <time.h>
3369793Sobrien#include <string.h>
3415702Sphk#include <stdlib.h>
3515702Sphk#include "vary.h"
3615773Sphk
3719813Sphkstruct trans {
3815702Sphk  int val;
3915702Sphk  char *str;
4015702Sphk};
4115773Sphk
4215773Sphkstatic struct trans trans_mon[] = {
4315773Sphk  { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
4415773Sphk  { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
4519813Sphk  { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
4619813Sphk  { -1, NULL }
4715702Sphk};
4815702Sphk
4915702Sphkstatic struct trans trans_wday[] = {
5015773Sphk  { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
5115773Sphk  { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
5215773Sphk  { -1, NULL }
5315702Sphk};
5415702Sphk
5515702Sphkstatic char digits[] = "0123456789";
5615702Sphkstatic int adjhour(struct tm *, char, int, int);
5715702Sphk
5815773Sphkstatic int
5915773Sphkdomktime(struct tm *t, char type)
6029526Scharnier{
6129526Scharnier  time_t ret;
6229526Scharnier
6315773Sphk  while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
6415773Sphk    /* While mktime() fails, adjust by an hour */
6515773Sphk    adjhour(t, type == '-' ? type : '+', 1, 0);
6615773Sphk
6715773Sphk  return ret;
6815773Sphk}
6915773Sphk
7015773Sphkstatic int
7115702Sphktrans(const struct trans t[], const char *arg)
7215702Sphk{
7319813Sphk  int f;
7419813Sphk
7515702Sphk  for (f = 0; t[f].val != -1; f++)
7619813Sphk    if (!strncasecmp(t[f].str, arg, 3) ||
7715702Sphk        !strncasecmp(t[f].str, arg, strlen(t[f].str)))
7819813Sphk      return t[f].val;
7915702Sphk
8019813Sphk  return -1;
8119813Sphk}
8215702Sphk
8319813Sphkstruct vary *
8415702Sphkvary_append(struct vary *v, char *arg)
8519813Sphk{
8615702Sphk  struct vary *result, **nextp;
8719813Sphk
8815702Sphk  if (v) {
8919813Sphk    result = v;
9019813Sphk    while (v->next)
9115702Sphk      v = v->next;
9219813Sphk    nextp = &v->next;
9315702Sphk  } else
9419813Sphk    nextp = &result;
9515702Sphk
9615702Sphk  *nextp = (struct vary *)malloc(sizeof(struct vary));
9715702Sphk  (*nextp)->arg = arg;
9815702Sphk  (*nextp)->next = NULL;
9915702Sphk  return result;
10019813Sphk}
10119813Sphk
10215702Sphkstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
10315702Sphk
10415702Sphkstatic int
10515702Sphkdaysinmonth(const struct tm *t)
10615702Sphk{
10715702Sphk  int year;
10815702Sphk
10915702Sphk  year = t->tm_year + 1900;
11019813Sphk
11129526Scharnier  if (t->tm_mon == 1)
11215702Sphk    if (!(year % 400))
11315702Sphk      return 29;
11415702Sphk    else if (!(year % 100))
11515702Sphk      return 28;
11615702Sphk    else if (!(year % 4))
11715702Sphk      return 29;
11819813Sphk    else
11919813Sphk      return 28;
12015702Sphk  else if (t->tm_mon >= 0 && t->tm_mon < 12)
12115702Sphk    return mdays[t->tm_mon];
12215702Sphk
12315702Sphk  return 0;
12415702Sphk}
12515702Sphk
12615702Sphk
12715702Sphkstatic int
12815702Sphkadjyear(struct tm *t, char type, int val, int mk)
12915702Sphk{
13019813Sphk  switch (type) {
13119813Sphk    case '+':
13219813Sphk      t->tm_year += val;
13315702Sphk      break;
13419813Sphk    case '-':
13515702Sphk      t->tm_year -= val;
13615702Sphk      break;
13719813Sphk    default:
13815773Sphk      t->tm_year = val;
13919813Sphk      if (t->tm_year < 69)
14015773Sphk      	t->tm_year += 100;		/* as per date.c */
14115702Sphk      else if (t->tm_year > 1900)
14215702Sphk        t->tm_year -= 1900;             /* struct tm holds years since 1900 */
14315702Sphk      break;
14415702Sphk  }
14515702Sphk  return !mk || domktime(t, type) != -1;
14615702Sphk}
14715702Sphk
14815702Sphkstatic int
14919813Sphkadjmon(struct tm *t, char type, int val, int istext, int mk)
15019813Sphk{
15115702Sphk  if (val < 0)
15215702Sphk    return 0;
15315702Sphk
15415702Sphk  switch (type) {
15515702Sphk    case '+':
15615702Sphk      if (istext) {
15719813Sphk        if (val <= t->tm_mon)
15819813Sphk          val += 11 - t->tm_mon;	/* early next year */
15919813Sphk        else
16019813Sphk          val -= t->tm_mon + 1;		/* later this year */
16115702Sphk      }
16219813Sphk      if (val) {
16319813Sphk        if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
16419813Sphk          return 0;
16519813Sphk        val %= 12;
16629526Scharnier        t->tm_mon += val;
16719813Sphk        if (t->tm_mon > 11)
16819813Sphk          t->tm_mon -= 12;
16919813Sphk      }
17019813Sphk      break;
17119813Sphk
17229526Scharnier    case '-':
17319813Sphk      if (istext) {
17415773Sphk        if (val-1 > t->tm_mon)
17515773Sphk          val = 13 - val + t->tm_mon;	/* later last year */
17615702Sphk        else
17715702Sphk          val = t->tm_mon - val + 1;	/* early this year */
17815702Sphk      }
17915702Sphk      if (val) {
18015702Sphk        if (!adjyear(t, '-', val / 12, 0))
18115702Sphk          return 0;
18215702Sphk        val %= 12;
18315702Sphk        if (val > t->tm_mon) {
18419813Sphk          if (!adjyear(t, '-', 1, 0))
18529526Scharnier            return 0;
18615702Sphk          val -= 12;
18715702Sphk        }
18819813Sphk        t->tm_mon -= val;
18929526Scharnier      }
19015702Sphk      break;
19115702Sphk
19215702Sphk    default:
19319813Sphk      if (val > 12 || val < 1)
19415702Sphk        return 0;
19515702Sphk      t->tm_mon = --val;
19615702Sphk  }
19715702Sphk
19815702Sphk  return !mk || domktime(t, type) != -1;
19915702Sphk}
20015773Sphk
20115702Sphkstatic int
20215702Sphkadjday(struct tm *t, char type, int val, int mk)
20315702Sphk{
20415702Sphk  int mdays;
20515702Sphk
20615702Sphk  switch (type) {
20715702Sphk    case '+':
20815702Sphk      while (val) {
20915702Sphk        mdays = daysinmonth(t);
21019813Sphk        if (val > mdays - t->tm_mday) {
21115702Sphk          val -= mdays - t->tm_mday + 1;
21215702Sphk          t->tm_mday = 1;
21315702Sphk          if (!adjmon(t, '+', 1, 0, 0))
21419813Sphk            return 0;
21515702Sphk        } else {
21615702Sphk          t->tm_mday += val;
21715702Sphk          val = 0;
21815702Sphk        }
21915773Sphk      }
22015773Sphk      break;
22115773Sphk    case '-':
22215773Sphk      while (val)
22315773Sphk        if (val >= t->tm_mday) {
22415773Sphk          val -= t->tm_mday;
22519912Sphk          t->tm_mday = 1;
22615773Sphk          if (!adjmon(t, '-', 1, 0, 0))
22715773Sphk            return 0;
22815773Sphk          t->tm_mday = daysinmonth(t);
22915773Sphk        } else {
23015773Sphk          t->tm_mday -= val;
23115773Sphk          val = 0;
23215773Sphk        }
23319912Sphk      break;
23415773Sphk    default:
23515773Sphk      if (val > 0 && val <= daysinmonth(t))
23615773Sphk        t->tm_mday = val;
23715773Sphk      else
23815773Sphk        return 0;
23915773Sphk      break;
24015773Sphk  }
24115773Sphk
24215773Sphk  return !mk || domktime(t, type) != -1;
24315773Sphk}
24415773Sphk
24515773Sphkstatic int
24615773Sphkadjwday(struct tm *t, char type, int val, int istext, int mk)
24715773Sphk{
24815773Sphk  if (val < 0)
24915773Sphk    return 0;
25015773Sphk
25115773Sphk  switch (type) {
25215773Sphk    case '+':
25315773Sphk      if (istext)
25415773Sphk        if (val < t->tm_wday)
25515773Sphk          val = 7 - t->tm_wday + val;  /* early next week */
25615773Sphk        else
25715773Sphk          val -= t->tm_wday;           /* later this week */
25815773Sphk      else
25915773Sphk        val *= 7;                      /* "-v+5w" == "5 weeks in the future" */
26015773Sphk      return !val || adjday(t, '+', val, mk);
26115773Sphk    case '-':
26215773Sphk      if (istext) {
26319813Sphk        if (val > t->tm_wday)
26415773Sphk          val = 7 - val + t->tm_wday;  /* later last week */
26515773Sphk        else
26615773Sphk          val = t->tm_wday - val;      /* early this week */
26715773Sphk      } else
26815773Sphk        val *= 7;                      /* "-v-5w" == "5 weeks ago" */
26915773Sphk      return !val || adjday(t, '-', val, mk);
27015773Sphk    default:
27115773Sphk      if (val < t->tm_wday)
27215773Sphk        return adjday(t, '-', t->tm_wday - val, mk);
27315773Sphk      else if (val > 6)
27415773Sphk        return 0;
27515773Sphk      else if (val > t->tm_wday)
27615773Sphk        return adjday(t, '+', val - t->tm_wday, mk);
27715773Sphk  }
27815702Sphk  return 1;
27919813Sphk}
28019813Sphk
28119813Sphkstatic int
28215773Sphkadjhour(struct tm *t, char type, int val, int mk)
28315702Sphk{
28415702Sphk  if (val < 0)
28515702Sphk    return 0;
28615702Sphk
28715773Sphk  switch (type) {
28819813Sphk    case '+':
28919813Sphk      if (val) {
29019813Sphk        int days;
29115773Sphk
29215702Sphk        days = (t->tm_hour + val) / 24;
29315702Sphk        val %= 24;
29415702Sphk        t->tm_hour += val;
29515702Sphk        t->tm_hour %= 24;
29615702Sphk        if (!adjday(t, '+', days, 0))
29719813Sphk          return 0;
29819813Sphk      }
29915702Sphk      break;
30015702Sphk
30115702Sphk    case '-':
30215702Sphk      if (val) {
30315702Sphk        int days;
30415702Sphk
30515773Sphk        days = val / 24;
30615702Sphk        val %= 24;
30715702Sphk        if (val > t->tm_hour) {
30819813Sphk          days++;
30919813Sphk          val -= 24;
31015702Sphk        }
31115702Sphk        t->tm_hour -= val;
31219813Sphk        if (!adjday(t, '-', days, 0))
31315702Sphk          return 0;
31415702Sphk      }
31515702Sphk      break;
31615702Sphk
31715702Sphk    default:
31815702Sphk      if (val > 23)
31915702Sphk        return 0;
32015702Sphk      t->tm_hour = val;
32119813Sphk  }
32219813Sphk
32319813Sphk  return !mk || domktime(t, type) != -1;
32419813Sphk}
32529526Scharnier
32619813Sphkstatic int
32719813Sphkadjmin(struct tm *t, char type, int val, int mk)
32829526Scharnier{
32915702Sphk  if (val < 0)
33015702Sphk    return 0;
33119813Sphk
33219813Sphk  switch (type) {
33319813Sphk    case '+':
33415702Sphk      if (val) {
33519813Sphk        if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
33615702Sphk          return 0;
33715702Sphk        val %= 60;
33815702Sphk        t->tm_min += val;
33915702Sphk        if (t->tm_min > 59)
34015702Sphk          t->tm_min -= 60;
34115702Sphk      }
34215702Sphk      break;
34315702Sphk
34415773Sphk    case '-':
34515773Sphk      if (val) {
34615702Sphk        if (!adjhour(t, '-', val / 60, 0))
34715702Sphk          return 0;
34819813Sphk        val %= 60;
34919813Sphk        if (val > t->tm_min) {
35019813Sphk          if (!adjhour(t, '-', 1, 0))
35119813Sphk            return 0;
35219813Sphk          val -= 60;
35319813Sphk        }
35419813Sphk        t->tm_min -= val;
35515702Sphk      }
35615702Sphk      break;
35715702Sphk
35815702Sphk    default:
35915702Sphk      if (val > 59)
36019813Sphk        return 0;
36119813Sphk      t->tm_min = val;
36219813Sphk  }
36315702Sphk
36419813Sphk  return !mk || domktime(t, type) != -1;
36519813Sphk}
36619813Sphk
36719813Sphkstatic int
36819813Sphkadjsec(struct tm *t, char type, int val, int mk)
36915702Sphk{
37015702Sphk  if (val < 0)
37115702Sphk    return 0;
37215702Sphk
37315702Sphk  switch (type) {
37415702Sphk    case '+':
37515702Sphk      if (val) {
37615702Sphk        if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
37715702Sphk          return 0;
37819816Sphk        val %= 60;
37915702Sphk        t->tm_sec += val;
38015702Sphk        if (t->tm_sec > 59)
38115702Sphk          t->tm_sec -= 60;
38215702Sphk      }
38315702Sphk      break;
38415702Sphk
38519816Sphk    case '-':
38619816Sphk      if (val) {
38719816Sphk        if (!adjmin(t, '-', val / 60, 0))
38819816Sphk          return 0;
38915773Sphk        val %= 60;
39019816Sphk        if (val > t->tm_sec) {
39115702Sphk          if (!adjmin(t, '-', 1, 0))
39219816Sphk            return 0;
39315773Sphk          val -= 60;
39419816Sphk        }
39515773Sphk        t->tm_sec -= val;
39615773Sphk      }
39719816Sphk      break;
39815702Sphk
39919816Sphk    default:
40019816Sphk      if (val > 59)
40119813Sphk        return 0;
40215773Sphk      t->tm_sec = val;
40319816Sphk  }
40419816Sphk
40519816Sphk  return !mk || domktime(t, type) != -1;
40619816Sphk}
40719816Sphk
40819816Sphkconst struct vary *
40919816Sphkvary_apply(const struct vary *v, struct tm *t)
41015702Sphk{
41115773Sphk  char type;
41215702Sphk  char which;
41315702Sphk  char *arg;
41415702Sphk  int len;
41515702Sphk  int val;
41615702Sphk
41715702Sphk  for (; v; v = v->next) {
41815702Sphk    type = *v->arg;
41915702Sphk    arg = v->arg;
42015773Sphk    if (type == '+' || type == '-')
42119813Sphk      arg++;
42215702Sphk    else
42315702Sphk      type = '\0';
42415702Sphk    len = strlen(arg);
42515702Sphk    if (len < 2)
42615702Sphk      return v;
42715702Sphk
42815702Sphk    if (type == '\0')
42919813Sphk      t->tm_isdst = -1;
43019813Sphk
43115702Sphk    if (strspn(arg, digits) != len-1) {
43215702Sphk      val = trans(trans_wday, arg);
43315702Sphk      if (val != -1) {
43419813Sphk          if (!adjwday(t, type, val, 1, 1))
43519813Sphk            return v;
43615702Sphk      } else {
43715702Sphk        val = trans(trans_mon, arg);
43815702Sphk        if (val != -1) {
43915702Sphk          if (!adjmon(t, type, val, 1, 1))
44015702Sphk            return v;
44115702Sphk        } else
44215702Sphk          return v;
44315773Sphk      }
44415773Sphk    } else {
44515773Sphk      val = atoi(arg);
44615702Sphk      which = arg[len-1];
44715702Sphk
44815702Sphk      switch (which) {
44915702Sphk        case 'S':
45015702Sphk          if (!adjsec(t, type, val, 1))
45115702Sphk            return v;
45215702Sphk          break;
45315702Sphk        case 'M':
45415702Sphk          if (!adjmin(t, type, val, 1))
45515702Sphk            return v;
45615702Sphk          break;
45715702Sphk        case 'H':
45815702Sphk          if (!adjhour(t, type, val, 1))
45915702Sphk            return v;
46015702Sphk          break;
46119813Sphk        case 'd':
46215702Sphk          t->tm_isdst = -1;
46315702Sphk          if (!adjday(t, type, val, 1))
46415702Sphk            return v;
46519813Sphk          break;
46615702Sphk        case 'w':
46715702Sphk          t->tm_isdst = -1;
46815702Sphk          if (!adjwday(t, type, val, 0, 1))
46915702Sphk            return v;
47015702Sphk          break;
47119813Sphk        case 'm':
47215702Sphk          t->tm_isdst = -1;
47315702Sphk          if (!adjmon(t, type, val, 0, 1))
47419813Sphk            return v;
47519813Sphk          break;
47615702Sphk        case 'y':
47715702Sphk          t->tm_isdst = -1;
47815702Sphk          if (!adjyear(t, type, val, 1))
47915702Sphk            return v;
48015702Sphk          break;
48119813Sphk        default:
48215702Sphk          return v;
48315702Sphk      }
48415702Sphk    }
48519813Sphk  }
48615702Sphk  return 0;
48715702Sphk}
48815702Sphk
48915702Sphkvoid
49015702Sphkvary_destroy(struct vary *v)
49115702Sphk{
49215702Sphk  struct vary *n;
49315702Sphk
49415702Sphk  while (v) {
49515702Sphk    n = v->next;
49615702Sphk    free(v);
49715702Sphk    v = n;
49815702Sphk  }
49915702Sphk}
50015773Sphk