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