vary.c revision 31921
1/*-
2 * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id$
27 */
28
29#include <time.h>
30#include <string.h>
31#include <stdlib.h>
32#include "vary.h"
33
34struct trans {
35  int val;
36  char *str;
37};
38
39static struct trans trans_mon[] = {
40  { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
41  { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" },
42  { 10, "october" }, { 11, "november" }, { 12, "december" },
43  { -1, NULL }
44};
45
46static struct trans trans_wday[] = {
47  { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
48  { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
49  { -1, NULL }
50};
51
52static char digits[] = "0123456789";
53
54static int
55trans(const struct trans t[], const char *arg)
56{
57  int f;
58
59  for (f = 0; t[f].val != -1; f++)
60    if (!strncasecmp(t[f].str, arg, 3) ||
61        !strncasecmp(t[f].str, arg, strlen(t[f].str)))
62      return t[f].val;
63
64  return -1;
65}
66
67struct vary *
68vary_append(struct vary *v, char *arg)
69{
70  struct vary *result, **nextp;
71
72  if (v) {
73    result = v;
74    while (v->next)
75      v = v->next;
76    nextp = &v->next;
77  } else
78    nextp = &result;
79
80  *nextp = (struct vary *)malloc(sizeof(struct vary));
81  (*nextp)->arg = arg;
82  (*nextp)->next = NULL;
83  return result;
84}
85
86static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
87
88static int
89daysinmonth(const struct tm *t)
90{
91  int year;
92
93  year = t->tm_year + 1900;
94
95  if (t->tm_mon == 1)
96    if (!(year % 400))
97      return 29;
98    else if (!(year % 100))
99      return 28;
100    else if (!(year % 4))
101      return 29;
102    else
103      return 28;
104  else if (t->tm_mon >= 0 && t->tm_mon < 12)
105    return mdays[t->tm_mon];
106
107  return 0;
108}
109
110
111static int
112adjyear(struct tm *t, char type, int val)
113{
114  switch (type) {
115    case '+':
116      t->tm_year += val;
117      break;
118    case '-':
119      t->tm_year -= val;
120      break;
121    default:
122      t->tm_year = val;
123      if (t->tm_year < 69)
124      	t->tm_year += 100;		/* as per date.c */
125      else if (t->tm_year > 1900)
126        t->tm_year -= 1900;             /* struct tm holds years since 1900 */
127      break;
128  }
129  return mktime(t) != -1;
130}
131
132static int
133adjmon(struct tm *t, char type, int val, int istext)
134{
135  if (val < 0)
136    return 0;
137
138  switch (type) {
139    case '+':
140      if (istext)
141        if (val <= t->tm_mon)
142          val += 11 - t->tm_mon;	/* early next year */
143        else
144          val -= t->tm_mon + 1;		/* later this year */
145      if (!adjyear(t, '+', (t->tm_mon + val) / 12))
146        return 0;
147      val %= 12;
148      t->tm_mon += val;
149      if (t->tm_mon > 11)
150        t->tm_mon -= 12;
151      break;
152
153    case '-':
154      if (istext)
155        if (val-1 > t->tm_mon)
156          val = 13 - val + t->tm_mon;	/* later last year */
157        else
158          val = t->tm_mon - val + 1;	/* early this year */
159      if (!adjyear(t, '-', val / 12))
160        return 0;
161      val %= 12;
162      if (val > t->tm_mon) {
163        if (!adjyear(t, '-', 1))
164          return 0;
165        val -= 12;
166      }
167      t->tm_mon -= val;
168      break;
169
170    default:
171      if (val > 12 || val < 1)
172        return 0;
173      t->tm_mon = --val;
174  }
175
176  return mktime(t) != -1;
177}
178
179static int
180adjday(struct tm *t, char type, int val)
181{
182  int mdays;
183  switch (type) {
184    case '+':
185      while (val) {
186        mdays = daysinmonth(t);
187        if (val > mdays - t->tm_mday) {
188          val -= mdays - t->tm_mday + 1;
189          t->tm_mday = 1;
190          if (!adjmon(t, '+', 1, 0))
191            return 0;
192        } else {
193          t->tm_mday += val;
194          val = 0;
195        }
196      }
197      break;
198    case '-':
199      while (val)
200        if (val >= t->tm_mday) {
201          val -= t->tm_mday;
202          t->tm_mday = 1;
203          if (!adjmon(t, '-', 1, 0))
204            return 0;
205          t->tm_mday = daysinmonth(t);
206        } else {
207          t->tm_mday -= val;
208          val = 0;
209        }
210      break;
211    default:
212      if (val > 0 && val <= daysinmonth(t))
213        t->tm_mday = val;
214      else
215        return 0;
216      break;
217  }
218
219  return mktime(t) != -1;
220}
221
222static int
223adjwday(struct tm *t, char type, int val, int istext)
224{
225  if (val < 0)
226    return 0;
227
228  switch (type) {
229    case '+':
230      if (istext)
231        if (val < t->tm_wday)
232          val = 7 - t->tm_wday + val;  /* early next week */
233        else
234          val -= t->tm_wday;           /* later this week */
235      else
236        val *= 7;                      /* "-W +5" == "5 weeks in the future" */
237      return adjday(t, '+', val);
238    case '-':
239      if (istext)
240        if (val > t->tm_wday)
241          val = 7 - val + t->tm_wday;  /* later last week */
242        else
243          val = t->tm_wday - val;      /* early this week */
244      else
245        val *= 7;                      /* "-W -5" == "5 weeks ago" */
246      return adjday(t, '-', val);
247    default:
248      if (val < t->tm_wday)
249        return adjday(t, '-', t->tm_wday - val);
250      else if (val > 6)
251        return 0;
252      else if (val > t->tm_wday)
253        return adjday(t, '+', val - t->tm_wday);
254  }
255  return 1;
256}
257
258static int
259adjhour(struct tm *t, char type, int val)
260{
261  if (val < 0)
262    return 0;
263
264  switch (type) {
265    case '+':
266      if (!adjday(t, '+', (t->tm_hour + val) / 24))
267        return 0;
268      val %= 24;
269      t->tm_hour += val;
270      if (t->tm_hour > 23)
271        t->tm_hour -= 24;
272      break;
273
274    case '-':
275      if (!adjday(t, '-', val / 24))
276        return 0;
277      val %= 24;
278      if (val > t->tm_hour) {
279        if (!adjday(t, '-', 1))
280          return 0;
281        val -= 24;
282      }
283      t->tm_hour -= val;
284      break;
285
286    default:
287      if (val > 23)
288        return 0;
289      t->tm_hour = val;
290  }
291
292  return mktime(t) != -1;
293}
294
295static int
296adjmin(struct tm *t, char type, int val)
297{
298  if (val < 0)
299    return 0;
300
301  switch (type) {
302    case '+':
303      if (!adjhour(t, '+', (t->tm_min + val) / 60))
304        return 0;
305      val %= 60;
306      t->tm_min += val;
307      if (t->tm_min > 59)
308        t->tm_min -= 60;
309      break;
310
311    case '-':
312      if (!adjhour(t, '-', val / 60))
313        return 0;
314      val %= 60;
315      if (val > t->tm_min) {
316        if (!adjhour(t, '-', 1))
317          return 0;
318        val -= 60;
319      }
320      t->tm_min -= val;
321      break;
322
323    default:
324      if (val > 59)
325        return 0;
326      t->tm_min = val;
327  }
328
329  return mktime(t) != -1;
330}
331
332const struct vary *
333vary_apply(const struct vary *v, struct tm *t)
334{
335  char type;
336  char which;
337  char *arg;
338  int len;
339  int val;
340
341  for (; v; v = v->next) {
342    type = *v->arg;
343    arg = v->arg;
344    if (type == '+' || type == '-')
345      arg++;
346    else
347      type = '\0';
348    len = strlen(arg);
349    if (len < 2)
350      return v;
351
352    if (strspn(arg, digits) != len-1) {
353      val = trans(trans_wday, arg);
354      if (val != -1) {
355          if (!adjwday(t, type, val, 1))
356            return v;
357      } else {
358        val = trans(trans_mon, arg);
359        if (val != -1) {
360          if (!adjmon(t, type, val, 1))
361            return v;
362        } else
363          return v;
364      }
365    } else {
366      val = atoi(arg);
367      which = arg[len-1];
368
369      switch (which) {
370        case 'M':
371          if (!adjmin(t, type, val))
372            return v;
373          break;
374        case 'H':
375          if (!adjhour(t, type, val))
376            return v;
377          break;
378        case 'd':
379          if (!adjday(t, type, val))
380            return v;
381          break;
382        case 'w':
383          if (!adjwday(t, type, val, 0))
384            return v;
385          break;
386        case 'm':
387          if (!adjmon(t, type, val, 0))
388            return v;
389          break;
390        case 'y':
391          if (!adjyear(t, type, val))
392            return v;
393          break;
394        default:
395          return v;
396      }
397    }
398  }
399  return 0;
400}
401
402void
403vary_destroy(struct vary *v)
404{
405  struct vary *n;
406
407  while (v) {
408    n = v->next;
409    free(v);
410    v = n;
411  }
412}
413