vary.c revision 99109
150276Speter/*-
250276Speter * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
350276Speter * All rights reserved.
450276Speter *
5184989Srafan * Redistribution and use in source and binary forms, with or without
650276Speter * modification, are permitted provided that the following conditions
797049Speter * are met:
8176187Srafan * 1. Redistributions of source code must retain the above copyright
997049Speter *    notice, this list of conditions and the following disclaimer.
1097049Speter * 2. Redistributions in binary form must reproduce the above copyright
1197049Speter *    notice, this list of conditions and the following disclaimer in the
1297049Speter *    documentation and/or other materials provided with the distribution.
1397049Speter *
1497049Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1597049Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1697049Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1797049Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1897049Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1997049Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2097049Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2197049Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2297049Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2397049Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2497049Speter * SUCH DAMAGE.
2597049Speter */
2697049Speter
2797049Speter#include <sys/cdefs.h>
2897049Speter__FBSDID("$FreeBSD: head/bin/date/vary.c 99109 2002-06-30 05:13:54Z obrien $");
2997049Speter
3097049Speter#include <err.h>
3197049Speter#include <time.h>
3297049Speter#include <string.h>
3397049Speter#include <stdlib.h>
3497049Speter#include "vary.h"
3550276Speter
3650276Speterstruct trans {
3750276Speter  int val;
3850276Speter  const char *str;
3950276Speter};
4050276Speter
4150276Speterstatic struct trans trans_mon[] = {
42166124Srafan  { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
4350276Speter  { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
4450276Speter  { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
4550276Speter  { -1, NULL }
4650276Speter};
4750276Speter
4850276Speterstatic struct trans trans_wday[] = {
49166124Srafan  { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
5050276Speter  { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
5150276Speter  { -1, NULL }
5250276Speter};
5350276Speter
5450276Speterstatic char digits[] = "0123456789";
5550276Speterstatic int adjhour(struct tm *, char, int, int);
5650276Speter
57166124Srafanstatic int
58166124Srafandomktime(struct tm *t, char type)
59166124Srafan{
60166124Srafan  time_t ret;
61166124Srafan
62166124Srafan  while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
63166124Srafan    /* While mktime() fails, adjust by an hour */
64166124Srafan    adjhour(t, type == '-' ? type : '+', 1, 0);
65174993Srafan
6650276Speter  return ret;
6797049Speter}
6850276Speter
6997049Speterstatic int
7097049Spetertrans(const struct trans t[], const char *arg)
7197049Speter{
7297049Speter  int f;
7397049Speter
7497049Speter  for (f = 0; t[f].val != -1; f++)
7597049Speter    if (!strncasecmp(t[f].str, arg, 3) ||
7697049Speter        !strncasecmp(t[f].str, arg, strlen(t[f].str)))
7797049Speter      return t[f].val;
7850276Speter
7997049Speter  return -1;
8097049Speter}
8197049Speter
8297049Speterstruct vary *
8397049Spetervary_append(struct vary *v, char *arg)
8497049Speter{
85166124Srafan  struct vary *result, **nextp;
8697049Speter
8797049Speter  if (v) {
8897049Speter    result = v;
89166124Srafan    while (v->next)
9097049Speter      v = v->next;
9150276Speter    nextp = &v->next;
9250276Speter  } else
93166124Srafan    nextp = &result;
9450276Speter
9550276Speter  if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL)
9650276Speter    err(1, "malloc");
97166124Srafan  (*nextp)->arg = arg;
9850276Speter  (*nextp)->next = NULL;
9950276Speter  return result;
10097049Speter}
10197049Speter
10297049Speterstatic int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
10397049Speter
10497049Speterstatic int
105166124Srafandaysinmonth(const struct tm *t)
10697049Speter{
10797049Speter  int year;
10897049Speter
109166124Srafan  year = t->tm_year + 1900;
11097049Speter
11197049Speter  if (t->tm_mon == 1)
11297049Speter    if (!(year % 400))
11350276Speter      return 29;
11450276Speter    else if (!(year % 100))
11550276Speter      return 28;
11650276Speter    else if (!(year % 4))
11750276Speter      return 29;
11850276Speter    else
11950276Speter      return 28;
12050276Speter  else if (t->tm_mon >= 0 && t->tm_mon < 12)
12150276Speter    return mdays[t->tm_mon];
12250276Speter
12350276Speter  return 0;
12450276Speter}
12550276Speter
12650276Speter
12750276Speterstatic int
12850276Speteradjyear(struct tm *t, char type, int val, int mk)
12950276Speter{
13050276Speter  switch (type) {
13150276Speter    case '+':
13250276Speter      t->tm_year += val;
13350276Speter      break;
13450276Speter    case '-':
13597049Speter      t->tm_year -= val;
13650276Speter      break;
137166124Srafan    default:
13850276Speter      t->tm_year = val;
13950276Speter      if (t->tm_year < 69)
14050276Speter      	t->tm_year += 100;		/* as per date.c */
14150276Speter      else if (t->tm_year > 1900)
14250276Speter        t->tm_year -= 1900;             /* struct tm holds years since 1900 */
143166124Srafan      break;
144166124Srafan  }
14550276Speter  return !mk || domktime(t, type) != -1;
14650276Speter}
14750276Speter
14850276Speterstatic int
14950276Speteradjmon(struct tm *t, char type, int val, int istext, int mk)
15050276Speter{
15197049Speter  if (val < 0)
15250276Speter    return 0;
15350276Speter
15450276Speter  switch (type) {
155184989Srafan    case '+':
15650276Speter      if (istext) {
15750276Speter        if (val <= t->tm_mon)
15850276Speter          val += 11 - t->tm_mon;	/* early next year */
15997049Speter        else
16097049Speter          val -= t->tm_mon + 1;		/* later this year */
16197049Speter      }
16297049Speter      if (val) {
16397049Speter        if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
16497049Speter          return 0;
16597049Speter        val %= 12;
16697049Speter        t->tm_mon += val;
16797049Speter        if (t->tm_mon > 11)
16897049Speter          t->tm_mon -= 12;
16997049Speter      }
17097049Speter      break;
17197049Speter
17297049Speter    case '-':
17397049Speter      if (istext) {
17497049Speter        if (val-1 > t->tm_mon)
17550276Speter          val = 13 - val + t->tm_mon;	/* later last year */
17650276Speter        else
17750276Speter          val = t->tm_mon - val + 1;	/* early this year */
17850276Speter      }
179166124Srafan      if (val) {
18050276Speter        if (!adjyear(t, '-', val / 12, 0))
18150276Speter          return 0;
18250276Speter        val %= 12;
18397049Speter        if (val > t->tm_mon) {
184166124Srafan          if (!adjyear(t, '-', 1, 0))
18550276Speter            return 0;
18650276Speter          val -= 12;
18797049Speter        }
18897049Speter        t->tm_mon -= val;
18950276Speter      }
19050276Speter      break;
19150276Speter
19250276Speter    default:
19397049Speter      if (val > 12 || val < 1)
19497049Speter        return 0;
19597049Speter      t->tm_mon = --val;
19697049Speter  }
19797049Speter
19897049Speter  return !mk || domktime(t, type) != -1;
19997049Speter}
20097049Speter
20197049Speterstatic int
20266963Speteradjday(struct tm *t, char type, int val, int mk)
20397049Speter{
20497049Speter  int lmdays;
20597049Speter
20697049Speter  switch (type) {
207166124Srafan    case '+':
208166124Srafan      while (val) {
209166124Srafan        lmdays = daysinmonth(t);
210166124Srafan        if (val > lmdays - t->tm_mday) {
21197049Speter          val -= lmdays - t->tm_mday + 1;
21297049Speter          t->tm_mday = 1;
21366963Speter          if (!adjmon(t, '+', 1, 0, 0))
21466963Speter            return 0;
21566963Speter        } else {
21697049Speter          t->tm_mday += val;
21797049Speter          val = 0;
21897049Speter        }
21997049Speter      }
22097049Speter      break;
22197049Speter    case '-':
22297049Speter      while (val)
22397049Speter        if (val >= t->tm_mday) {
22497049Speter          val -= t->tm_mday;
22597049Speter          t->tm_mday = 1;
22650276Speter          if (!adjmon(t, '-', 1, 0, 0))
22750276Speter            return 0;
22850276Speter          t->tm_mday = daysinmonth(t);
22997049Speter        } else {
23097049Speter          t->tm_mday -= val;
23150276Speter          val = 0;
23250276Speter        }
23350276Speter      break;
23450276Speter    default:
23550276Speter      if (val > 0 && val <= daysinmonth(t))
23650276Speter        t->tm_mday = val;
23750276Speter      else
23850276Speter        return 0;
23950276Speter      break;
24050276Speter  }
24197049Speter
24250276Speter  return !mk || domktime(t, type) != -1;
24397049Speter}
24497049Speter
24550276Speterstatic int
24697049Speteradjwday(struct tm *t, char type, int val, int istext, int mk)
24797049Speter{
24897049Speter  if (val < 0)
24997049Speter    return 0;
25097049Speter
25197049Speter  switch (type) {
25297049Speter    case '+':
25397049Speter      if (istext)
25497049Speter        if (val < t->tm_wday)
255184989Srafan          val = 7 - t->tm_wday + val;  /* early next week */
25650276Speter        else
25750276Speter          val -= t->tm_wday;           /* later this week */
25850276Speter      else
25950276Speter        val *= 7;                      /* "-v+5w" == "5 weeks in the future" */
260166124Srafan      return !val || adjday(t, '+', val, mk);
261166124Srafan    case '-':
26250276Speter      if (istext) {
26397049Speter        if (val > t->tm_wday)
26450276Speter          val = 7 - val + t->tm_wday;  /* later last week */
26550276Speter        else
26650276Speter          val = t->tm_wday - val;      /* early this week */
26750276Speter      } else
268166124Srafan        val *= 7;                      /* "-v-5w" == "5 weeks ago" */
269166124Srafan      return !val || adjday(t, '-', val, mk);
270166124Srafan    default:
27150276Speter      if (val < t->tm_wday)
27250276Speter        return adjday(t, '-', t->tm_wday - val, mk);
27350276Speter      else if (val > 6)
27450276Speter        return 0;
27550276Speter      else if (val > t->tm_wday)
27650276Speter        return adjday(t, '+', val - t->tm_wday, mk);
27750276Speter  }
27850276Speter  return 1;
27950276Speter}
28050276Speter
28150276Speterstatic int
28250276Speteradjhour(struct tm *t, char type, int val, int mk)
283166124Srafan{
284166124Srafan  if (val < 0)
285166124Srafan    return 0;
286166124Srafan
287166124Srafan  switch (type) {
28850276Speter    case '+':
28950276Speter      if (val) {
29050276Speter        int days;
29150276Speter
29250276Speter        days = (t->tm_hour + val) / 24;
29350276Speter        val %= 24;
29450276Speter        t->tm_hour += val;
29550276Speter        t->tm_hour %= 24;
29650276Speter        if (!adjday(t, '+', days, 0))
29750276Speter          return 0;
29850276Speter      }
29950276Speter      break;
30050276Speter
30150276Speter    case '-':
30250276Speter      if (val) {
30350276Speter        int days;
30450276Speter
30550276Speter        days = val / 24;
306166124Srafan        val %= 24;
30750276Speter        if (val > t->tm_hour) {
308166124Srafan          days++;
309166124Srafan          val -= 24;
310166124Srafan        }
311166124Srafan        t->tm_hour -= val;
312166124Srafan        if (!adjday(t, '-', days, 0))
313166124Srafan          return 0;
314166124Srafan      }
315166124Srafan      break;
316166124Srafan
31750276Speter    default:
31850276Speter      if (val > 23)
31950276Speter        return 0;
32050276Speter      t->tm_hour = val;
32150276Speter  }
32250276Speter
32350276Speter  return !mk || domktime(t, type) != -1;
32450276Speter}
32550276Speter
32650276Speterstatic int
32750276Speteradjmin(struct tm *t, char type, int val, int mk)
32850276Speter{
32950276Speter  if (val < 0)
33050276Speter    return 0;
33150276Speter
33250276Speter  switch (type) {
33350276Speter    case '+':
33450276Speter      if (val) {
33550276Speter        if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
33650276Speter          return 0;
33750276Speter        val %= 60;
33850276Speter        t->tm_min += val;
33966963Speter        if (t->tm_min > 59)
34050276Speter          t->tm_min -= 60;
341184989Srafan      }
34250276Speter      break;
34397049Speter
34497049Speter    case '-':
34597049Speter      if (val) {
34697049Speter        if (!adjhour(t, '-', val / 60, 0))
34797049Speter          return 0;
34897049Speter        val %= 60;
34997049Speter        if (val > t->tm_min) {
35097049Speter          if (!adjhour(t, '-', 1, 0))
35197049Speter            return 0;
35250276Speter          val -= 60;
35397049Speter        }
35450276Speter        t->tm_min -= val;
35550276Speter      }
35650276Speter      break;
35750276Speter
35850276Speter    default:
35950276Speter      if (val > 59)
36050276Speter        return 0;
361184989Srafan      t->tm_min = val;
36250276Speter  }
36350276Speter
36450276Speter  return !mk || domktime(t, type) != -1;
36550276Speter}
36697049Speter
36797049Speterstatic int
36897049Speteradjsec(struct tm *t, char type, int val, int mk)
36997049Speter{
37097049Speter  if (val < 0)
37197049Speter    return 0;
37297049Speter
37397049Speter  switch (type) {
37497049Speter    case '+':
37597049Speter      if (val) {
37697049Speter        if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
37797049Speter          return 0;
37897049Speter        val %= 60;
37997049Speter        t->tm_sec += val;
38097049Speter        if (t->tm_sec > 59)
38197049Speter          t->tm_sec -= 60;
38297049Speter      }
383166124Srafan      break;
38497049Speter
38597049Speter    case '-':
38697049Speter      if (val) {
38797049Speter        if (!adjmin(t, '-', val / 60, 0))
38897049Speter          return 0;
38997049Speter        val %= 60;
39097049Speter        if (val > t->tm_sec) {
39197049Speter          if (!adjmin(t, '-', 1, 0))
39297049Speter            return 0;
39397049Speter          val -= 60;
39497049Speter        }
39597049Speter        t->tm_sec -= val;
39697049Speter      }
39797049Speter      break;
398166124Srafan
39997049Speter    default:
40097049Speter      if (val > 59)
40197049Speter        return 0;
40297049Speter      t->tm_sec = val;
40397049Speter  }
40497049Speter
40576726Speter  return !mk || domktime(t, type) != -1;
40676726Speter}
40776726Speter
40897049Speterconst struct vary *
409174993Srafanvary_apply(const struct vary *v, struct tm *t)
410174993Srafan{
411174993Srafan  char type;
412174993Srafan  char which;
413174993Srafan  char *arg;
41497049Speter  size_t len;
41597049Speter  int val;
416166124Srafan
417166124Srafan  for (; v; v = v->next) {
418166124Srafan    type = *v->arg;
419184989Srafan    arg = v->arg;
42097049Speter    if (type == '+' || type == '-')
42150276Speter      arg++;
42250276Speter    else
42350276Speter      type = '\0';
42476726Speter    len = strlen(arg);
425176187Srafan    if (len < 2)
426184989Srafan      return v;
427184989Srafan
428184989Srafan    if (type == '\0')
42997049Speter      t->tm_isdst = -1;
430
431    if (strspn(arg, digits) != len-1) {
432      val = trans(trans_wday, arg);
433      if (val != -1) {
434          if (!adjwday(t, type, val, 1, 1))
435            return v;
436      } else {
437        val = trans(trans_mon, arg);
438        if (val != -1) {
439          if (!adjmon(t, type, val, 1, 1))
440            return v;
441        } else
442          return v;
443      }
444    } else {
445      val = atoi(arg);
446      which = arg[len-1];
447
448      switch (which) {
449        case 'S':
450          if (!adjsec(t, type, val, 1))
451            return v;
452          break;
453        case 'M':
454          if (!adjmin(t, type, val, 1))
455            return v;
456          break;
457        case 'H':
458          if (!adjhour(t, type, val, 1))
459            return v;
460          break;
461        case 'd':
462          t->tm_isdst = -1;
463          if (!adjday(t, type, val, 1))
464            return v;
465          break;
466        case 'w':
467          t->tm_isdst = -1;
468          if (!adjwday(t, type, val, 0, 1))
469            return v;
470          break;
471        case 'm':
472          t->tm_isdst = -1;
473          if (!adjmon(t, type, val, 0, 1))
474            return v;
475          break;
476        case 'y':
477          t->tm_isdst = -1;
478          if (!adjyear(t, type, val, 1))
479            return v;
480          break;
481        default:
482          return v;
483      }
484    }
485  }
486  return 0;
487}
488
489void
490vary_destroy(struct vary *v)
491{
492  struct vary *n;
493
494  while (v) {
495    n = v->next;
496    free(v);
497    v = n;
498  }
499}
500