vary.c revision 69457
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 69457 2000-12-01 09:59:40Z brian $";
3035773Scharnier#endif /* not lint */
3135773Scharnier
3269457Sbrian#include <err.h>
3327874Sbrian#include <time.h>
3427874Sbrian#include <string.h>
3527874Sbrian#include <stdlib.h>
3627874Sbrian#include "vary.h"
3727874Sbrian
3827874Sbrianstruct trans {
3927874Sbrian  int val;
4027874Sbrian  char *str;
4127874Sbrian};
4227874Sbrian
4327874Sbrianstatic struct trans trans_mon[] = {
4427874Sbrian  { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
4557326Salfred  { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
4657326Salfred  { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
4727874Sbrian  { -1, NULL }
4827874Sbrian};
4927874Sbrian
5027874Sbrianstatic struct trans trans_wday[] = {
5127874Sbrian  { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
5227874Sbrian  { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
5327874Sbrian  { -1, NULL }
5427874Sbrian};
5527874Sbrian
5627874Sbrianstatic char digits[] = "0123456789";
5759175Sbrianstatic int adjhour(struct tm *, char, int, int);
5827874Sbrian
5927874Sbrianstatic int
6059022Sbriandomktime(struct tm *t, char type)
6159022Sbrian{
6259175Sbrian  time_t ret;
6359022Sbrian
6459175Sbrian  while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
6559175Sbrian    /* While mktime() fails, adjust by an hour */
6659175Sbrian    adjhour(t, type == '-' ? type : '+', 1, 0);
6759022Sbrian
6859022Sbrian  return ret;
6959022Sbrian}
7059022Sbrian
7159022Sbrianstatic int
7227874Sbriantrans(const struct trans t[], const char *arg)
7327874Sbrian{
7427874Sbrian  int f;
7527874Sbrian
7627874Sbrian  for (f = 0; t[f].val != -1; f++)
7728025Sbrian    if (!strncasecmp(t[f].str, arg, 3) ||
7828025Sbrian        !strncasecmp(t[f].str, arg, strlen(t[f].str)))
7927874Sbrian      return t[f].val;
8027874Sbrian
8127874Sbrian  return -1;
8227874Sbrian}
8327874Sbrian
8427874Sbrianstruct vary *
8528025Sbrianvary_append(struct vary *v, char *arg)
8627874Sbrian{
8727874Sbrian  struct vary *result, **nextp;
8827874Sbrian
8927874Sbrian  if (v) {
9027874Sbrian    result = v;
9127874Sbrian    while (v->next)
9227874Sbrian      v = v->next;
9327874Sbrian    nextp = &v->next;
9427874Sbrian  } else
9527874Sbrian    nextp = &result;
9627874Sbrian
9769457Sbrian  if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL)
9869457Sbrian    err(1, "malloc");
9927874Sbrian  (*nextp)->arg = arg;
10027874Sbrian  (*nextp)->next = NULL;
10127874Sbrian  return result;
10227874Sbrian}
10327874Sbrian
10427874Sbrianstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
10527874Sbrian
10627874Sbrianstatic int
10727874Sbriandaysinmonth(const struct tm *t)
10827874Sbrian{
10927874Sbrian  int year;
11027874Sbrian
11127874Sbrian  year = t->tm_year + 1900;
11227874Sbrian
11327874Sbrian  if (t->tm_mon == 1)
11427874Sbrian    if (!(year % 400))
11527874Sbrian      return 29;
11627874Sbrian    else if (!(year % 100))
11727874Sbrian      return 28;
11827874Sbrian    else if (!(year % 4))
11927874Sbrian      return 29;
12027874Sbrian    else
12127874Sbrian      return 28;
12227874Sbrian  else if (t->tm_mon >= 0 && t->tm_mon < 12)
12327874Sbrian    return mdays[t->tm_mon];
12427874Sbrian
12527874Sbrian  return 0;
12627874Sbrian}
12727874Sbrian
12827874Sbrian
12927874Sbrianstatic int
13059175Sbrianadjyear(struct tm *t, char type, int val, int mk)
13127874Sbrian{
13227874Sbrian  switch (type) {
13327874Sbrian    case '+':
13427874Sbrian      t->tm_year += val;
13527874Sbrian      break;
13627874Sbrian    case '-':
13727874Sbrian      t->tm_year -= val;
13827874Sbrian      break;
13927874Sbrian    default:
14027874Sbrian      t->tm_year = val;
14127874Sbrian      if (t->tm_year < 69)
14227874Sbrian      	t->tm_year += 100;		/* as per date.c */
14327874Sbrian      else if (t->tm_year > 1900)
14427874Sbrian        t->tm_year -= 1900;             /* struct tm holds years since 1900 */
14527874Sbrian      break;
14627874Sbrian  }
14759175Sbrian  return !mk || domktime(t, type) != -1;
14827874Sbrian}
14927874Sbrian
15027874Sbrianstatic int
15159175Sbrianadjmon(struct tm *t, char type, int val, int istext, int mk)
15227874Sbrian{
15327874Sbrian  if (val < 0)
15427874Sbrian    return 0;
15527874Sbrian
15627874Sbrian  switch (type) {
15727874Sbrian    case '+':
15846073Simp      if (istext) {
15927874Sbrian        if (val <= t->tm_mon)
16027874Sbrian          val += 11 - t->tm_mon;	/* early next year */
16127874Sbrian        else
16227874Sbrian          val -= t->tm_mon + 1;		/* later this year */
16346073Simp      }
16459022Sbrian      if (val) {
16559175Sbrian        if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
16659022Sbrian          return 0;
16759022Sbrian        val %= 12;
16859022Sbrian        t->tm_mon += val;
16959022Sbrian        if (t->tm_mon > 11)
17059022Sbrian          t->tm_mon -= 12;
17159022Sbrian      }
17227874Sbrian      break;
17327874Sbrian
17427874Sbrian    case '-':
17546073Simp      if (istext) {
17627874Sbrian        if (val-1 > t->tm_mon)
17727874Sbrian          val = 13 - val + t->tm_mon;	/* later last year */
17827874Sbrian        else
17927874Sbrian          val = t->tm_mon - val + 1;	/* early this year */
18046073Simp      }
18159022Sbrian      if (val) {
18259175Sbrian        if (!adjyear(t, '-', val / 12, 0))
18327874Sbrian          return 0;
18459022Sbrian        val %= 12;
18559022Sbrian        if (val > t->tm_mon) {
18659175Sbrian          if (!adjyear(t, '-', 1, 0))
18759022Sbrian            return 0;
18859022Sbrian          val -= 12;
18959022Sbrian        }
19059022Sbrian        t->tm_mon -= val;
19127874Sbrian      }
19227874Sbrian      break;
19327874Sbrian
19427874Sbrian    default:
19527874Sbrian      if (val > 12 || val < 1)
19627874Sbrian        return 0;
19727874Sbrian      t->tm_mon = --val;
19827874Sbrian  }
19927874Sbrian
20059175Sbrian  return !mk || domktime(t, type) != -1;
20127874Sbrian}
20227874Sbrian
20327874Sbrianstatic int
20459175Sbrianadjday(struct tm *t, char type, int val, int mk)
20527874Sbrian{
20627874Sbrian  int mdays;
20759022Sbrian
20827874Sbrian  switch (type) {
20927874Sbrian    case '+':
21027874Sbrian      while (val) {
21127874Sbrian        mdays = daysinmonth(t);
21227874Sbrian        if (val > mdays - t->tm_mday) {
21327874Sbrian          val -= mdays - t->tm_mday + 1;
21427874Sbrian          t->tm_mday = 1;
21559175Sbrian          if (!adjmon(t, '+', 1, 0, 0))
21627874Sbrian            return 0;
21727874Sbrian        } else {
21827874Sbrian          t->tm_mday += val;
21927874Sbrian          val = 0;
22027874Sbrian        }
22127874Sbrian      }
22227874Sbrian      break;
22327874Sbrian    case '-':
22427874Sbrian      while (val)
22527874Sbrian        if (val >= t->tm_mday) {
22627874Sbrian          val -= t->tm_mday;
22727874Sbrian          t->tm_mday = 1;
22859175Sbrian          if (!adjmon(t, '-', 1, 0, 0))
22927874Sbrian            return 0;
23027874Sbrian          t->tm_mday = daysinmonth(t);
23127874Sbrian        } else {
23227874Sbrian          t->tm_mday -= val;
23327874Sbrian          val = 0;
23427874Sbrian        }
23527874Sbrian      break;
23627874Sbrian    default:
23727874Sbrian      if (val > 0 && val <= daysinmonth(t))
23827874Sbrian        t->tm_mday = val;
23927874Sbrian      else
24027874Sbrian        return 0;
24127874Sbrian      break;
24227874Sbrian  }
24327874Sbrian
24459175Sbrian  return !mk || domktime(t, type) != -1;
24527874Sbrian}
24627874Sbrian
24727874Sbrianstatic int
24859175Sbrianadjwday(struct tm *t, char type, int val, int istext, int mk)
24927874Sbrian{
25027874Sbrian  if (val < 0)
25127874Sbrian    return 0;
25227874Sbrian
25328025Sbrian  switch (type) {
25427874Sbrian    case '+':
25527874Sbrian      if (istext)
25627874Sbrian        if (val < t->tm_wday)
25727874Sbrian          val = 7 - t->tm_wday + val;  /* early next week */
25827874Sbrian        else
25927874Sbrian          val -= t->tm_wday;           /* later this week */
26027874Sbrian      else
26159022Sbrian        val *= 7;                      /* "-v+5w" == "5 weeks in the future" */
26260836Sbrian      return !val || adjday(t, '+', val, mk);
26327874Sbrian    case '-':
26459022Sbrian      if (istext) {
26527874Sbrian        if (val > t->tm_wday)
26627874Sbrian          val = 7 - val + t->tm_wday;  /* later last week */
26727874Sbrian        else
26827874Sbrian          val = t->tm_wday - val;      /* early this week */
26959022Sbrian      } else
27059022Sbrian        val *= 7;                      /* "-v-5w" == "5 weeks ago" */
27160836Sbrian      return !val || adjday(t, '-', val, mk);
27227874Sbrian    default:
27327874Sbrian      if (val < t->tm_wday)
27460836Sbrian        return adjday(t, '-', t->tm_wday - val, mk);
27527874Sbrian      else if (val > 6)
27627874Sbrian        return 0;
27727874Sbrian      else if (val > t->tm_wday)
27860836Sbrian        return adjday(t, '+', val - t->tm_wday, mk);
27927874Sbrian  }
28027874Sbrian  return 1;
28127874Sbrian}
28227874Sbrian
28327874Sbrianstatic int
28459175Sbrianadjhour(struct tm *t, char type, int val, int mk)
28527874Sbrian{
28628025Sbrian  if (val < 0)
28728025Sbrian    return 0;
28828025Sbrian
28928025Sbrian  switch (type) {
29027874Sbrian    case '+':
29159022Sbrian      if (val) {
29259022Sbrian        int days;
29359022Sbrian
29459022Sbrian        days = (t->tm_hour + val) / 24;
29559022Sbrian        val %= 24;
29659022Sbrian        t->tm_hour += val;
29759022Sbrian        t->tm_hour %= 24;
29859175Sbrian        if (!adjday(t, '+', days, 0))
29959022Sbrian          return 0;
30059022Sbrian      }
30128025Sbrian      break;
30228025Sbrian
30327874Sbrian    case '-':
30459022Sbrian      if (val) {
30559022Sbrian        int days;
30659022Sbrian
30759022Sbrian        days = val / 24;
30859022Sbrian        val %= 24;
30959022Sbrian        if (val > t->tm_hour) {
31059022Sbrian          days++;
31159022Sbrian          val -= 24;
31259022Sbrian        }
31359022Sbrian        t->tm_hour -= val;
31459175Sbrian        if (!adjday(t, '-', days, 0))
31528025Sbrian          return 0;
31628025Sbrian      }
31728025Sbrian      break;
31828025Sbrian
31927874Sbrian    default:
32028025Sbrian      if (val > 23)
32128025Sbrian        return 0;
32228025Sbrian      t->tm_hour = val;
32327874Sbrian  }
32428025Sbrian
32559175Sbrian  return !mk || domktime(t, type) != -1;
32627874Sbrian}
32727874Sbrian
32828025Sbrianstatic int
32959175Sbrianadjmin(struct tm *t, char type, int val, int mk)
33028025Sbrian{
33128025Sbrian  if (val < 0)
33228025Sbrian    return 0;
33328025Sbrian
33428025Sbrian  switch (type) {
33528025Sbrian    case '+':
33659022Sbrian      if (val) {
33759175Sbrian        if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
33859022Sbrian          return 0;
33959022Sbrian        val %= 60;
34059022Sbrian        t->tm_min += val;
34159022Sbrian        if (t->tm_min > 59)
34259022Sbrian          t->tm_min -= 60;
34359022Sbrian      }
34428025Sbrian      break;
34528025Sbrian
34628025Sbrian    case '-':
34759022Sbrian      if (val) {
34859175Sbrian        if (!adjhour(t, '-', val / 60, 0))
34928025Sbrian          return 0;
35059022Sbrian        val %= 60;
35159022Sbrian        if (val > t->tm_min) {
35259175Sbrian          if (!adjhour(t, '-', 1, 0))
35359022Sbrian            return 0;
35459022Sbrian          val -= 60;
35559022Sbrian        }
35659022Sbrian        t->tm_min -= val;
35728025Sbrian      }
35828025Sbrian      break;
35928025Sbrian
36028025Sbrian    default:
36128025Sbrian      if (val > 59)
36228025Sbrian        return 0;
36328025Sbrian      t->tm_min = val;
36428025Sbrian  }
36528025Sbrian
36659175Sbrian  return !mk || domktime(t, type) != -1;
36728025Sbrian}
36828025Sbrian
36944598Sbrianstatic int
37059175Sbrianadjsec(struct tm *t, char type, int val, int mk)
37144598Sbrian{
37244598Sbrian  if (val < 0)
37344598Sbrian    return 0;
37444598Sbrian
37544598Sbrian  switch (type) {
37644598Sbrian    case '+':
37759022Sbrian      if (val) {
37859175Sbrian        if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
37959022Sbrian          return 0;
38059022Sbrian        val %= 60;
38159022Sbrian        t->tm_sec += val;
38259022Sbrian        if (t->tm_sec > 59)
38359022Sbrian          t->tm_sec -= 60;
38459022Sbrian      }
38544598Sbrian      break;
38644598Sbrian
38744598Sbrian    case '-':
38859022Sbrian      if (val) {
38959175Sbrian        if (!adjmin(t, '-', val / 60, 0))
39044598Sbrian          return 0;
39159022Sbrian        val %= 60;
39259022Sbrian        if (val > t->tm_sec) {
39359175Sbrian          if (!adjmin(t, '-', 1, 0))
39459022Sbrian            return 0;
39559022Sbrian          val -= 60;
39659022Sbrian        }
39759022Sbrian        t->tm_sec -= val;
39844598Sbrian      }
39944598Sbrian      break;
40044598Sbrian
40144598Sbrian    default:
40244598Sbrian      if (val > 59)
40344598Sbrian        return 0;
40444598Sbrian      t->tm_sec = val;
40544598Sbrian  }
40644598Sbrian
40759175Sbrian  return !mk || domktime(t, type) != -1;
40844598Sbrian}
40944598Sbrian
41027874Sbrianconst struct vary *
41127874Sbrianvary_apply(const struct vary *v, struct tm *t)
41227874Sbrian{
41328025Sbrian  char type;
41428025Sbrian  char which;
41528025Sbrian  char *arg;
41628025Sbrian  int len;
41728025Sbrian  int val;
41828025Sbrian
41927874Sbrian  for (; v; v = v->next) {
42028025Sbrian    type = *v->arg;
42128025Sbrian    arg = v->arg;
42228025Sbrian    if (type == '+' || type == '-')
42328025Sbrian      arg++;
42428025Sbrian    else
42528025Sbrian      type = '\0';
42628025Sbrian    len = strlen(arg);
42728025Sbrian    if (len < 2)
42828025Sbrian      return v;
42928025Sbrian
43059175Sbrian    if (type == '\0')
43159175Sbrian      t->tm_isdst = -1;
43259175Sbrian
43328025Sbrian    if (strspn(arg, digits) != len-1) {
43428025Sbrian      val = trans(trans_wday, arg);
43528025Sbrian      if (val != -1) {
43659175Sbrian          if (!adjwday(t, type, val, 1, 1))
43728025Sbrian            return v;
43828025Sbrian      } else {
43928025Sbrian        val = trans(trans_mon, arg);
44028025Sbrian        if (val != -1) {
44159175Sbrian          if (!adjmon(t, type, val, 1, 1))
44228025Sbrian            return v;
44328025Sbrian        } else
44427874Sbrian          return v;
44528025Sbrian      }
44628025Sbrian    } else {
44728025Sbrian      val = atoi(arg);
44828025Sbrian      which = arg[len-1];
44928025Sbrian
45028025Sbrian      switch (which) {
45144598Sbrian        case 'S':
45259175Sbrian          if (!adjsec(t, type, val, 1))
45344598Sbrian            return v;
45444598Sbrian          break;
45528025Sbrian        case 'M':
45659175Sbrian          if (!adjmin(t, type, val, 1))
45728025Sbrian            return v;
45828025Sbrian          break;
45928025Sbrian        case 'H':
46059175Sbrian          if (!adjhour(t, type, val, 1))
46128025Sbrian            return v;
46228025Sbrian          break;
46328025Sbrian        case 'd':
46459175Sbrian          t->tm_isdst = -1;
46559175Sbrian          if (!adjday(t, type, val, 1))
46628025Sbrian            return v;
46728025Sbrian          break;
46828025Sbrian        case 'w':
46959175Sbrian          t->tm_isdst = -1;
47059175Sbrian          if (!adjwday(t, type, val, 0, 1))
47128025Sbrian            return v;
47228025Sbrian          break;
47328025Sbrian        case 'm':
47459175Sbrian          t->tm_isdst = -1;
47559175Sbrian          if (!adjmon(t, type, val, 0, 1))
47628025Sbrian            return v;
47728025Sbrian          break;
47828025Sbrian        case 'y':
47959175Sbrian          t->tm_isdst = -1;
48059175Sbrian          if (!adjyear(t, type, val, 1))
48128025Sbrian            return v;
48228025Sbrian          break;
48328025Sbrian        default:
48427874Sbrian          return v;
48528025Sbrian      }
48627874Sbrian    }
48727874Sbrian  }
48827874Sbrian  return 0;
48927874Sbrian}
49027874Sbrian
49127874Sbrianvoid
49227874Sbrianvary_destroy(struct vary *v)
49327874Sbrian{
49427874Sbrian  struct vary *n;
49527874Sbrian
49627874Sbrian  while (v) {
49727874Sbrian    n = v->next;
49827874Sbrian    free(v);
49927874Sbrian    v = n;
50027874Sbrian  }
50127874Sbrian}
502