vary.c revision 50471
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
27#ifndef lint
28static const char rcsid[] =
29  "$FreeBSD: head/bin/date/vary.c 50471 1999-08-27 23:15:48Z peter $";
30#endif /* not lint */
31
32#include <time.h>
33#include <string.h>
34#include <stdlib.h>
35#include "vary.h"
36
37struct trans {
38  int val;
39  char *str;
40};
41
42static struct trans trans_mon[] = {
43  { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
44  { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" },
45  { 10, "october" }, { 11, "november" }, { 12, "december" },
46  { -1, NULL }
47};
48
49static struct trans trans_wday[] = {
50  { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
51  { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
52  { -1, NULL }
53};
54
55static char digits[] = "0123456789";
56
57static int
58trans(const struct trans t[], const char *arg)
59{
60  int f;
61
62  for (f = 0; t[f].val != -1; f++)
63    if (!strncasecmp(t[f].str, arg, 3) ||
64        !strncasecmp(t[f].str, arg, strlen(t[f].str)))
65      return t[f].val;
66
67  return -1;
68}
69
70struct vary *
71vary_append(struct vary *v, char *arg)
72{
73  struct vary *result, **nextp;
74
75  if (v) {
76    result = v;
77    while (v->next)
78      v = v->next;
79    nextp = &v->next;
80  } else
81    nextp = &result;
82
83  *nextp = (struct vary *)malloc(sizeof(struct vary));
84  (*nextp)->arg = arg;
85  (*nextp)->next = NULL;
86  return result;
87}
88
89static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
90
91static int
92daysinmonth(const struct tm *t)
93{
94  int year;
95
96  year = t->tm_year + 1900;
97
98  if (t->tm_mon == 1)
99    if (!(year % 400))
100      return 29;
101    else if (!(year % 100))
102      return 28;
103    else if (!(year % 4))
104      return 29;
105    else
106      return 28;
107  else if (t->tm_mon >= 0 && t->tm_mon < 12)
108    return mdays[t->tm_mon];
109
110  return 0;
111}
112
113
114static int
115adjyear(struct tm *t, char type, int val)
116{
117  switch (type) {
118    case '+':
119      t->tm_year += val;
120      break;
121    case '-':
122      t->tm_year -= val;
123      break;
124    default:
125      t->tm_year = val;
126      if (t->tm_year < 69)
127      	t->tm_year += 100;		/* as per date.c */
128      else if (t->tm_year > 1900)
129        t->tm_year -= 1900;             /* struct tm holds years since 1900 */
130      break;
131  }
132  return mktime(t) != -1;
133}
134
135static int
136adjmon(struct tm *t, char type, int val, int istext)
137{
138  if (val < 0)
139    return 0;
140
141  switch (type) {
142    case '+':
143      if (istext) {
144        if (val <= t->tm_mon)
145          val += 11 - t->tm_mon;	/* early next year */
146        else
147          val -= t->tm_mon + 1;		/* later this year */
148      }
149      if (!adjyear(t, '+', (t->tm_mon + val) / 12))
150        return 0;
151      val %= 12;
152      t->tm_mon += val;
153      if (t->tm_mon > 11)
154        t->tm_mon -= 12;
155      break;
156
157    case '-':
158      if (istext) {
159        if (val-1 > t->tm_mon)
160          val = 13 - val + t->tm_mon;	/* later last year */
161        else
162          val = t->tm_mon - val + 1;	/* early this year */
163      }
164      if (!adjyear(t, '-', val / 12))
165        return 0;
166      val %= 12;
167      if (val > t->tm_mon) {
168        if (!adjyear(t, '-', 1))
169          return 0;
170        val -= 12;
171      }
172      t->tm_mon -= val;
173      break;
174
175    default:
176      if (val > 12 || val < 1)
177        return 0;
178      t->tm_mon = --val;
179  }
180
181  return mktime(t) != -1;
182}
183
184static int
185adjday(struct tm *t, char type, int val)
186{
187  int mdays;
188  switch (type) {
189    case '+':
190      while (val) {
191        mdays = daysinmonth(t);
192        if (val > mdays - t->tm_mday) {
193          val -= mdays - t->tm_mday + 1;
194          t->tm_mday = 1;
195          if (!adjmon(t, '+', 1, 0))
196            return 0;
197        } else {
198          t->tm_mday += val;
199          val = 0;
200        }
201      }
202      break;
203    case '-':
204      while (val)
205        if (val >= t->tm_mday) {
206          val -= t->tm_mday;
207          t->tm_mday = 1;
208          if (!adjmon(t, '-', 1, 0))
209            return 0;
210          t->tm_mday = daysinmonth(t);
211        } else {
212          t->tm_mday -= val;
213          val = 0;
214        }
215      break;
216    default:
217      if (val > 0 && val <= daysinmonth(t))
218        t->tm_mday = val;
219      else
220        return 0;
221      break;
222  }
223
224  return mktime(t) != -1;
225}
226
227static int
228adjwday(struct tm *t, char type, int val, int istext)
229{
230  if (val < 0)
231    return 0;
232
233  switch (type) {
234    case '+':
235      if (istext)
236        if (val < t->tm_wday)
237          val = 7 - t->tm_wday + val;  /* early next week */
238        else
239          val -= t->tm_wday;           /* later this week */
240      else
241        val *= 7;                      /* "-W +5" == "5 weeks in the future" */
242      return adjday(t, '+', val);
243    case '-':
244      if (istext)
245        if (val > t->tm_wday)
246          val = 7 - val + t->tm_wday;  /* later last week */
247        else
248          val = t->tm_wday - val;      /* early this week */
249      else
250        val *= 7;                      /* "-W -5" == "5 weeks ago" */
251      return adjday(t, '-', val);
252    default:
253      if (val < t->tm_wday)
254        return adjday(t, '-', t->tm_wday - val);
255      else if (val > 6)
256        return 0;
257      else if (val > t->tm_wday)
258        return adjday(t, '+', val - t->tm_wday);
259  }
260  return 1;
261}
262
263static int
264adjhour(struct tm *t, char type, int val)
265{
266  if (val < 0)
267    return 0;
268
269  switch (type) {
270    case '+':
271      if (!adjday(t, '+', (t->tm_hour + val) / 24))
272        return 0;
273      val %= 24;
274      t->tm_hour += val;
275      if (t->tm_hour > 23)
276        t->tm_hour -= 24;
277      break;
278
279    case '-':
280      if (!adjday(t, '-', val / 24))
281        return 0;
282      val %= 24;
283      if (val > t->tm_hour) {
284        if (!adjday(t, '-', 1))
285          return 0;
286        val -= 24;
287      }
288      t->tm_hour -= val;
289      break;
290
291    default:
292      if (val > 23)
293        return 0;
294      t->tm_hour = val;
295  }
296
297  return mktime(t) != -1;
298}
299
300static int
301adjmin(struct tm *t, char type, int val)
302{
303  if (val < 0)
304    return 0;
305
306  switch (type) {
307    case '+':
308      if (!adjhour(t, '+', (t->tm_min + val) / 60))
309        return 0;
310      val %= 60;
311      t->tm_min += val;
312      if (t->tm_min > 59)
313        t->tm_min -= 60;
314      break;
315
316    case '-':
317      if (!adjhour(t, '-', val / 60))
318        return 0;
319      val %= 60;
320      if (val > t->tm_min) {
321        if (!adjhour(t, '-', 1))
322          return 0;
323        val -= 60;
324      }
325      t->tm_min -= val;
326      break;
327
328    default:
329      if (val > 59)
330        return 0;
331      t->tm_min = val;
332  }
333
334  return mktime(t) != -1;
335}
336
337static int
338adjsec(struct tm *t, char type, int val)
339{
340  if (val < 0)
341    return 0;
342
343  switch (type) {
344    case '+':
345      if (!adjmin(t, '+', (t->tm_sec + val) / 60))
346        return 0;
347      val %= 60;
348      t->tm_sec += val;
349      if (t->tm_sec > 59)
350        t->tm_sec -= 60;
351      break;
352
353    case '-':
354      if (!adjmin(t, '-', val / 60))
355        return 0;
356      val %= 60;
357      if (val > t->tm_sec) {
358        if (!adjmin(t, '-', 1))
359          return 0;
360        val -= 60;
361      }
362      t->tm_sec -= val;
363      break;
364
365    default:
366      if (val > 59)
367        return 0;
368      t->tm_sec = val;
369  }
370
371  return mktime(t) != -1;
372}
373
374const struct vary *
375vary_apply(const struct vary *v, struct tm *t)
376{
377  char type;
378  char which;
379  char *arg;
380  int len;
381  int val;
382
383  for (; v; v = v->next) {
384    type = *v->arg;
385    arg = v->arg;
386    if (type == '+' || type == '-')
387      arg++;
388    else
389      type = '\0';
390    len = strlen(arg);
391    if (len < 2)
392      return v;
393
394    if (strspn(arg, digits) != len-1) {
395      val = trans(trans_wday, arg);
396      if (val != -1) {
397          if (!adjwday(t, type, val, 1))
398            return v;
399      } else {
400        val = trans(trans_mon, arg);
401        if (val != -1) {
402          if (!adjmon(t, type, val, 1))
403            return v;
404        } else
405          return v;
406      }
407    } else {
408      val = atoi(arg);
409      which = arg[len-1];
410
411      switch (which) {
412        case 'S':
413          if (!adjsec(t, type, val))
414            return v;
415          break;
416        case 'M':
417          if (!adjmin(t, type, val))
418            return v;
419          break;
420        case 'H':
421          if (!adjhour(t, type, val))
422            return v;
423          break;
424        case 'd':
425          if (!adjday(t, type, val))
426            return v;
427          break;
428        case 'w':
429          if (!adjwday(t, type, val, 0))
430            return v;
431          break;
432        case 'm':
433          if (!adjmon(t, type, val, 0))
434            return v;
435          break;
436        case 'y':
437          if (!adjyear(t, type, val))
438            return v;
439          break;
440        default:
441          return v;
442      }
443    }
444  }
445  return 0;
446}
447
448void
449vary_destroy(struct vary *v)
450{
451  struct vary *n;
452
453  while (v) {
454    n = v->next;
455    free(v);
456    v = n;
457  }
458}
459