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