vary.c revision 27874
127874Sbrian#include <time.h>
227874Sbrian#include <string.h>
327874Sbrian#include <stdlib.h>
427874Sbrian#include "vary.h"
527874Sbrian
627874Sbrianstruct trans {
727874Sbrian  int val;
827874Sbrian  char *str;
927874Sbrian};
1027874Sbrian
1127874Sbrianstatic struct trans trans_mon[] = {
1227874Sbrian  { 1, "jan" }, { 2, "feb" }, { 3, "mar" }, { 4, "apr" }, { 5, "may" },
1327874Sbrian  { 6, "jun" }, { 7, "jul" }, { 8, "aug" }, { 9, "sep" }, { 10, "oct" },
1427874Sbrian  { 11, "nov" }, { 12, "dec" },
1527874Sbrian  { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
1627874Sbrian  { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" },
1727874Sbrian  { 10, "october" }, { 11, "november" }, { 12, "december" },
1827874Sbrian  { -1, NULL }
1927874Sbrian};
2027874Sbrian
2127874Sbrianstatic struct trans trans_wday[] = {
2227874Sbrian  { 0, "sun" }, { 1, "mon" }, { 2, "tue" }, { 3, "wed" }, { 4, "thr" },
2327874Sbrian  { 4, "thu" }, { 5, "fri" }, { 6, "sat" },
2427874Sbrian  { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
2527874Sbrian  { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
2627874Sbrian  { -1, NULL }
2727874Sbrian};
2827874Sbrian
2927874Sbrianstatic char digits[] = "0123456789";
3027874Sbrian
3127874Sbrianstatic int
3227874Sbriantrans(const struct trans t[], const char *arg)
3327874Sbrian{
3427874Sbrian  int f;
3527874Sbrian
3627874Sbrian  if (strspn(arg, digits) == strlen(arg))
3727874Sbrian    return atoi(arg);
3827874Sbrian
3927874Sbrian  for (f = 0; t[f].val != -1; f++)
4027874Sbrian    if (!strcasecmp(t[f].str, arg))
4127874Sbrian      return t[f].val;
4227874Sbrian
4327874Sbrian  return -1;
4427874Sbrian}
4527874Sbrian
4627874Sbrianstruct vary *
4727874Sbrianvary_append(struct vary *v, char flag, char *arg)
4827874Sbrian{
4927874Sbrian  struct vary *result, **nextp;
5027874Sbrian
5127874Sbrian  if (!strchr("DWMY", flag))
5227874Sbrian    return 0;
5327874Sbrian
5427874Sbrian  if (v) {
5527874Sbrian    result = v;
5627874Sbrian    while (v->next)
5727874Sbrian      v = v->next;
5827874Sbrian    nextp = &v->next;
5927874Sbrian  } else
6027874Sbrian    nextp = &result;
6127874Sbrian
6227874Sbrian  *nextp = (struct vary *)malloc(sizeof(struct vary));
6327874Sbrian  (*nextp)->flag = flag;
6427874Sbrian  (*nextp)->arg = arg;
6527874Sbrian  (*nextp)->next = NULL;
6627874Sbrian  return result;
6727874Sbrian}
6827874Sbrian
6927874Sbrianstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
7027874Sbrian
7127874Sbrianstatic int
7227874Sbriandaysinmonth(const struct tm *t)
7327874Sbrian{
7427874Sbrian  int year;
7527874Sbrian
7627874Sbrian  year = t->tm_year + 1900;
7727874Sbrian
7827874Sbrian  if (t->tm_mon == 1)
7927874Sbrian    if (!(year % 400))
8027874Sbrian      return 29;
8127874Sbrian    else if (!(year % 100))
8227874Sbrian      return 28;
8327874Sbrian    else if (!(year % 4))
8427874Sbrian      return 29;
8527874Sbrian    else
8627874Sbrian      return 28;
8727874Sbrian  else if (t->tm_mon >= 0 && t->tm_mon < 12)
8827874Sbrian    return mdays[t->tm_mon];
8927874Sbrian
9027874Sbrian  return 0;
9127874Sbrian}
9227874Sbrian
9327874Sbrian
9427874Sbrianstatic int
9527874Sbrianadjyear(struct tm *t, char type, int val)
9627874Sbrian{
9727874Sbrian  switch (type) {
9827874Sbrian    case '+':
9927874Sbrian      t->tm_year += val;
10027874Sbrian      break;
10127874Sbrian    case '-':
10227874Sbrian      t->tm_year -= val;
10327874Sbrian      break;
10427874Sbrian    default:
10527874Sbrian      t->tm_year = val;
10627874Sbrian      if (t->tm_year < 69)
10727874Sbrian      	t->tm_year += 100;		/* as per date.c */
10827874Sbrian      else if (t->tm_year > 1900)
10927874Sbrian        t->tm_year -= 1900;             /* struct tm holds years since 1900 */
11027874Sbrian      break;
11127874Sbrian  }
11227874Sbrian  return mktime(t) != -1;
11327874Sbrian}
11427874Sbrian
11527874Sbrianstatic int
11627874Sbriansadjyear(struct tm *t, char *arg)
11727874Sbrian{
11827874Sbrian  switch (*arg) {
11927874Sbrian    case '+':
12027874Sbrian    case '-':
12127874Sbrian      return adjyear(t, *arg, atoi(arg+1));
12227874Sbrian    default:
12327874Sbrian      return adjyear(t, '\0', atoi(arg));
12427874Sbrian  }
12527874Sbrian}
12627874Sbrian
12727874Sbrianstatic int
12827874Sbrianadjmon(struct tm *t, char type, int val, int istext)
12927874Sbrian{
13027874Sbrian  if (val < 0)
13127874Sbrian    return 0;
13227874Sbrian
13327874Sbrian  switch (type) {
13427874Sbrian    case '+':
13527874Sbrian      if (istext)
13627874Sbrian        if (val <= t->tm_mon)
13727874Sbrian          val += 11 - t->tm_mon;	/* early next year */
13827874Sbrian        else
13927874Sbrian          val -= t->tm_mon + 1;		/* later this year */
14027874Sbrian      if (!adjyear(t, '+', (t->tm_mon + val) / 12))
14127874Sbrian        return 0;
14227874Sbrian      val %= 12;
14327874Sbrian      t->tm_mon += val;
14427874Sbrian      if (t->tm_mon > 11)
14527874Sbrian        t->tm_mon -= 12;
14627874Sbrian      break;
14727874Sbrian
14827874Sbrian    case '-':
14927874Sbrian      if (istext)
15027874Sbrian        if (val-1 > t->tm_mon)
15127874Sbrian          val = 13 - val + t->tm_mon;	/* later last year */
15227874Sbrian        else
15327874Sbrian          val = t->tm_mon - val + 1;	/* early this year */
15427874Sbrian      if (!adjyear(t, '-', val / 12))
15527874Sbrian        return 0;
15627874Sbrian      val %= 12;
15727874Sbrian      if (val > t->tm_mon) {
15827874Sbrian        if (!adjyear(t, '-', 1))
15927874Sbrian          return 0;
16027874Sbrian        val -= 12;
16127874Sbrian      }
16227874Sbrian      t->tm_mon -= val;
16327874Sbrian      break;
16427874Sbrian
16527874Sbrian    default:
16627874Sbrian      if (val > 12 || val < 1)
16727874Sbrian        return 0;
16827874Sbrian      t->tm_mon = --val;
16927874Sbrian  }
17027874Sbrian
17127874Sbrian  return mktime(t) != -1;
17227874Sbrian}
17327874Sbrian
17427874Sbrianstatic int
17527874Sbriansadjmon(struct tm *t, char *arg)
17627874Sbrian{
17727874Sbrian  int istext;
17827874Sbrian  int val;
17927874Sbrian
18027874Sbrian  switch (*arg) {
18127874Sbrian    case '+':
18227874Sbrian    case '-':
18327874Sbrian      istext = strspn(arg+1, digits) != strlen(arg+1);
18427874Sbrian      val = trans(trans_mon, arg+1);
18527874Sbrian      return adjmon(t, *arg, val, istext);
18627874Sbrian    default:
18727874Sbrian      istext = strspn(arg, digits) != strlen(arg);
18827874Sbrian      val = trans(trans_mon, arg);
18927874Sbrian      return adjmon(t, '\0', val, istext);
19027874Sbrian  }
19127874Sbrian}
19227874Sbrian
19327874Sbrianstatic int
19427874Sbrianadjday(struct tm *t, char type, int val)
19527874Sbrian{
19627874Sbrian  int mdays;
19727874Sbrian  switch (type) {
19827874Sbrian    case '+':
19927874Sbrian      while (val) {
20027874Sbrian        mdays = daysinmonth(t);
20127874Sbrian        if (val > mdays - t->tm_mday) {
20227874Sbrian          val -= mdays - t->tm_mday + 1;
20327874Sbrian          t->tm_mday = 1;
20427874Sbrian          if (!adjmon(t, '+', 1, 0))
20527874Sbrian            return 0;
20627874Sbrian        } else {
20727874Sbrian          t->tm_mday += val;
20827874Sbrian          val = 0;
20927874Sbrian        }
21027874Sbrian      }
21127874Sbrian      break;
21227874Sbrian    case '-':
21327874Sbrian      while (val)
21427874Sbrian        if (val >= t->tm_mday) {
21527874Sbrian          val -= t->tm_mday;
21627874Sbrian          t->tm_mday = 1;
21727874Sbrian          if (!adjmon(t, '-', 1, 0))
21827874Sbrian            return 0;
21927874Sbrian          t->tm_mday = daysinmonth(t);
22027874Sbrian        } else {
22127874Sbrian          t->tm_mday -= val;
22227874Sbrian          val = 0;
22327874Sbrian        }
22427874Sbrian      break;
22527874Sbrian    default:
22627874Sbrian      if (val > 0 && val <= daysinmonth(t))
22727874Sbrian        t->tm_mday = val;
22827874Sbrian      else
22927874Sbrian        return 0;
23027874Sbrian      break;
23127874Sbrian  }
23227874Sbrian
23327874Sbrian  return mktime(t) != -1;
23427874Sbrian}
23527874Sbrian
23627874Sbrianstatic int
23727874Sbriansadjwday(struct tm *t, char *arg)
23827874Sbrian{
23927874Sbrian  int istext;
24027874Sbrian  int val;
24127874Sbrian
24227874Sbrian  switch (*arg) {
24327874Sbrian    case '+':
24427874Sbrian    case '-':
24527874Sbrian      istext = strspn(arg+1, digits) != strlen(arg+1);
24627874Sbrian      val = trans(trans_wday, arg+1);
24727874Sbrian      break;
24827874Sbrian    default:
24927874Sbrian      istext = 0;
25027874Sbrian      val = trans(trans_wday, arg);
25127874Sbrian      break;
25227874Sbrian  }
25327874Sbrian
25427874Sbrian  if (val < 0)
25527874Sbrian    return 0;
25627874Sbrian
25727874Sbrian  switch (*arg) {
25827874Sbrian    case '+':
25927874Sbrian      if (istext)
26027874Sbrian        if (val < t->tm_wday)
26127874Sbrian          val = 7 - t->tm_wday + val;  /* early next week */
26227874Sbrian        else
26327874Sbrian          val -= t->tm_wday;           /* later this week */
26427874Sbrian      else
26527874Sbrian        val *= 7;                      /* "-W +5" == "5 weeks in the future" */
26627874Sbrian      return adjday(t, '+', val);
26727874Sbrian    case '-':
26827874Sbrian      if (istext)
26927874Sbrian        if (val > t->tm_wday)
27027874Sbrian          val = 7 - val + t->tm_wday;  /* later last week */
27127874Sbrian        else
27227874Sbrian          val = t->tm_wday - val;      /* early this week */
27327874Sbrian      else
27427874Sbrian        val *= 7;                      /* "-W -5" == "5 weeks ago" */
27527874Sbrian      return adjday(t, '-', val);
27627874Sbrian    default:
27727874Sbrian      if (val < t->tm_wday)
27827874Sbrian        return adjday(t, '-', t->tm_wday - val);
27927874Sbrian      else if (val > 6)
28027874Sbrian        return 0;
28127874Sbrian      else if (val > t->tm_wday)
28227874Sbrian        return adjday(t, '+', val - t->tm_wday);
28327874Sbrian  }
28427874Sbrian  return 1;
28527874Sbrian}
28627874Sbrian
28727874Sbrianstatic int
28827874Sbriansadjday(struct tm *t, char *arg)
28927874Sbrian{
29027874Sbrian  switch (*arg) {
29127874Sbrian    case '+':
29227874Sbrian    case '-':
29327874Sbrian      return adjday(t, *arg, atoi(arg+1));
29427874Sbrian    default:
29527874Sbrian      return adjday(t, '\0', atoi(arg));
29627874Sbrian  }
29727874Sbrian}
29827874Sbrian
29927874Sbrianconst struct vary *
30027874Sbrianvary_apply(const struct vary *v, struct tm *t)
30127874Sbrian{
30227874Sbrian  for (; v; v = v->next) {
30327874Sbrian    switch (v->flag) {
30427874Sbrian      case 'D':
30527874Sbrian        if (!sadjday(t, v->arg))
30627874Sbrian          return v;
30727874Sbrian        break;
30827874Sbrian      case 'W':
30927874Sbrian        if (!sadjwday(t, v->arg))
31027874Sbrian          return v;
31127874Sbrian        break;
31227874Sbrian      case 'M':
31327874Sbrian        if (!sadjmon(t, v->arg))
31427874Sbrian          return v;
31527874Sbrian        break;
31627874Sbrian      case 'Y':
31727874Sbrian        if (!sadjyear(t, v->arg))
31827874Sbrian          return v;
31927874Sbrian        break;
32027874Sbrian      default:
32127874Sbrian        return v;
32227874Sbrian    }
32327874Sbrian  }
32427874Sbrian  return 0;
32527874Sbrian}
32627874Sbrian
32727874Sbrianvoid
32827874Sbrianvary_destroy(struct vary *v)
32927874Sbrian{
33027874Sbrian  struct vary *n;
33127874Sbrian
33227874Sbrian  while (v) {
33327874Sbrian    n = v->next;
33427874Sbrian    free(v);
33527874Sbrian    v = n;
33627874Sbrian  }
33727874Sbrian}
338