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