Annotation of src/usr.bin/calendar/day.c, Revision 1.6
1.6 ! pjanzen 1: /* $OpenBSD: day.c,v 1.5 1998/11/04 11:32:02 pjanzen Exp $ */
1.1 millert 2:
3: /*
4: * Copyright (c) 1989, 1993, 1994
5: * The Regents of the University of California. All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: * 3. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #ifndef lint
37: static const char copyright[] =
38: "@(#) Copyright (c) 1989, 1993\n\
39: The Regents of the University of California. All rights reserved.\n";
40: #endif /* not lint */
41:
42: #ifndef lint
43: #if 0
44: static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94";
45: #else
1.6 ! pjanzen 46: static char rcsid[] = "$OpenBSD: day.c,v 1.5 1998/11/04 11:32:02 pjanzen Exp $";
1.1 millert 47: #endif
48: #endif /* not lint */
49:
50: #include <sys/types.h>
51: #include <sys/uio.h>
52:
53: #include <ctype.h>
54: #include <err.h>
55: #include <locale.h>
56: #include <stdio.h>
57: #include <stdlib.h>
58: #include <string.h>
59: #include <time.h>
60: #include <tzfile.h>
61:
62: #include "pathnames.h"
63: #include "calendar.h"
64:
65: struct tm *tp;
66: int *cumdays, offset, yrdays;
67: char dayname[10];
68:
69:
70: /* 1-based month, 0-based days, cumulative */
71: int daytab[][14] = {
72: { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 },
73: { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
74: };
75:
76: static char *days[] = {
77: "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
78: };
79:
80: static char *months[] = {
81: "jan", "feb", "mar", "apr", "may", "jun",
82: "jul", "aug", "sep", "oct", "nov", "dec", NULL,
83: };
84:
85: static struct fixs fndays[8]; /* full national days names */
86: static struct fixs ndays[8]; /* short national days names */
87:
88: static struct fixs fnmonths[13]; /* full national months names */
89: static struct fixs nmonths[13]; /* short national month names */
90:
91:
92: void setnnames(void)
93: {
94: char buf[80];
95: int i, l;
96: struct tm tm;
97:
98: for (i = 0; i < 7; i++) {
99: tm.tm_wday = i;
1.3 millert 100: l = strftime(buf, sizeof(buf), "%a", &tm);
101: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 102: ;
103: buf[l] = '\0';
104: if (ndays[i].name != NULL)
105: free(ndays[i].name);
106: if ((ndays[i].name = strdup(buf)) == NULL)
107: errx(1, "cannot allocate memory");
108: ndays[i].len = strlen(buf);
109:
1.3 millert 110: l = strftime(buf, sizeof(buf), "%A", &tm);
111: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 112: ;
113: buf[l] = '\0';
114: if (fndays[i].name != NULL)
115: free(fndays[i].name);
116: if ((fndays[i].name = strdup(buf)) == NULL)
117: errx(1, "cannot allocate memory");
118: fndays[i].len = strlen(buf);
119: }
120:
121: for (i = 0; i < 12; i++) {
122: tm.tm_mon = i;
1.3 millert 123: l = strftime(buf, sizeof(buf), "%b", &tm);
124: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 125: ;
126: buf[l] = '\0';
127: if (nmonths[i].name != NULL)
128: free(nmonths[i].name);
129: if ((nmonths[i].name = strdup(buf)) == NULL)
130: errx(1, "cannot allocate memory");
131: nmonths[i].len = strlen(buf);
132:
1.3 millert 133: l = strftime(buf, sizeof(buf), "%B", &tm);
134: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 135: ;
136: buf[l] = '\0';
137: if (fnmonths[i].name != NULL)
138: free(fnmonths[i].name);
139: if ((fnmonths[i].name = strdup(buf)) == NULL)
140: errx(1, "cannot allocate memory");
141: fnmonths[i].len = strlen(buf);
142: }
143: }
144:
145: void
146: settime(now)
147: time_t now;
148: {
149: tp = localtime(&now);
1.4 deraadt 150: if (isleap(tp->tm_year + TM_YEAR_BASE)) {
1.1 millert 151: yrdays = DAYSPERLYEAR;
152: cumdays = daytab[1];
153: } else {
154: yrdays = DAYSPERNYEAR;
155: cumdays = daytab[0];
156: }
157: /* Friday displays Monday's events */
158: offset = tp->tm_wday == 5 ? 3 : 1;
159: header[5].iov_base = dayname;
160:
161: (void) setlocale(LC_TIME, "C");
162: header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
163: (void) setlocale(LC_TIME, "");
164:
165: setnnames();
166: }
167:
1.2 millert 168: /* convert [Year][Month]Day into unix time (since 1970)
169: * Year: two or four digits, Month: two digits, Day: two digits
1.1 millert 170: */
171: time_t Mktime (date)
172: char *date;
173: {
174: time_t t;
175: int len;
176: struct tm tm;
177:
178: (void)time(&t);
179: tp = localtime(&t);
180:
181: len = strlen(date);
1.2 millert 182: if (len < 2)
183: return((time_t)-1);
1.1 millert 184: tm.tm_sec = 0;
185: tm.tm_min = 0;
1.5 pjanzen 186: /* Avoid getting caught by a timezone shift; set time to noon */
187: tm.tm_isdst = 0;
188: tm.tm_hour = 12;
1.1 millert 189: tm.tm_wday = 0;
190: tm.tm_mday = tp->tm_mday;
191: tm.tm_mon = tp->tm_mon;
192: tm.tm_year = tp->tm_year;
193:
1.2 millert 194: /* Day */
195: tm.tm_mday = atoi(date + len - 2);
1.1 millert 196:
1.2 millert 197: /* Month */
1.1 millert 198: if (len >= 4) {
1.2 millert 199: *(date + len - 2) = '\0';
200: tm.tm_mon = atoi(date + len - 4) - 1;
1.1 millert 201: }
202:
203: /* Year */
1.5 pjanzen 204: if (len >= 6) {
1.2 millert 205: *(date + len - 4) = '\0';
206: tm.tm_year = atoi(date);
1.1 millert 207:
1.4 deraadt 208: /* tm_year up TM_YEAR_BASE ... */
1.5 pjanzen 209: if (tm.tm_year < 69) /* Y2K */
1.4 deraadt 210: tm.tm_year += 2000 - TM_YEAR_BASE;
211: else if (tm.tm_year < 100)
212: tm.tm_year += 1900 - TM_YEAR_BASE;
213: else if (tm.tm_year > TM_YEAR_BASE)
214: tm.tm_year -= TM_YEAR_BASE;
1.1 millert 215: }
216:
217: #if DEBUG
218: printf("Mktime: %d %d %d %s\n", (int)mktime(&tm), (int)t, len,
219: asctime(&tm));
220: #endif
221: return(mktime(&tm));
222: }
223:
224: /*
225: * Possible date formats include any combination of:
226: * 3-charmonth (January, Jan, Jan)
227: * 3-charweekday (Friday, Monday, mon.)
228: * numeric month or day (1, 2, 04)
229: *
230: * Any character may separate them, or they may not be separated. Any line,
231: * following a line that is matched, that starts with "whitespace", is shown
232: * along with the matched line.
233: */
1.6 ! pjanzen 234: struct match *
! 235: isnow(endp)
1.1 millert 236: char *endp;
237: {
238: int day, flags = 0, month = 0, v1, v2;
1.6 ! pjanzen 239: int monthp, dayp, varp;
! 240: struct match *matches;
1.1 millert 241:
242: /*
243: * CONVENTION
244: *
245: * Month: 1-12
246: * Monthname: Jan .. Dec
247: * Day: 1-31
248: * Weekday: Mon-Sun
249: *
250: */
251:
252: /* read first field */
253: /* didn't recognize anything, skip it */
254: if (!(v1 = getfield(endp, &endp, &flags)))
1.6 ! pjanzen 255: return (NULL);
1.1 millert 256:
257: /* Easter or Easter depending days */
258: if (flags & F_EASTER)
259: day = v1 - 1; /* days since January 1 [0-365] */
260:
261: /*
262: * 1. {Weekday,Day} XYZ ...
263: *
264: * where Day is > 12
265: */
266: else if (flags & F_ISDAY || v1 > 12) {
267:
268: /* found a day; day: 1-31 or weekday: 1-7 */
269: day = v1;
270:
271: /* {Day,Weekday} {Month,Monthname} ... */
272: /* if no recognizable month, assume just a day alone
273: * in other words, find month or use current month */
274: if (!(month = getfield(endp, &endp, &flags)))
275: month = tp->tm_mon + 1;
276: }
277:
278: /* 2. {Monthname} XYZ ... */
279: else if (flags & F_ISMONTH) {
280: month = v1;
281:
282: /* Monthname {day,weekday} */
283: /* if no recognizable day, assume the first day in month */
284: if (!(day = getfield(endp, &endp, &flags)))
285: day = 1;
286: }
287:
288: /* Hm ... */
289: else {
290: v2 = getfield(endp, &endp, &flags);
291:
292: /*
293: * {Day} {Monthname} ...
294: * where Day <= 12
295: */
296: if (flags & F_ISMONTH) {
297: day = v1;
298: month = v2;
1.6 ! pjanzen 299: varp = 0;
1.1 millert 300: }
301:
302: /* {Month} {Weekday,Day} ... */
303: else {
304: /* F_ISDAY set, v2 > 12, or no way to tell */
305: month = v1;
306: /* if no recognizable day, assume the first */
307: day = v2 ? v2 : 1;
1.6 ! pjanzen 308: varp = 0;
1.1 millert 309: }
310: }
311:
312: /* convert Weekday into *next* Day,
313: * e.g.: 'Sunday' -> 22
1.5 pjanzen 314: * 'SundayLast' -> ??
1.1 millert 315: */
316: if (flags & F_ISDAY) {
317: #if DEBUG
318: fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month);
319: #endif
320:
1.6 ! pjanzen 321: varp = 1;
1.1 millert 322: /* variable weekday, SundayLast, MondayFirst ... */
323: if (day < 0 || day >= 10) {
324:
325: /* negative offset; last, -4 .. -1 */
326: if (day < 0) {
327: v1 = day/10 - 1; /* offset -4 ... -1 */
1.5 pjanzen 328: day = 10 + (day % 10); /* day 1 ... 7 */
1.1 millert 329:
1.5 pjanzen 330: /* which weekday the end of the month is (1-7) */
331: v2 = (cumdays[month + 1] - tp->tm_yday +
332: tp->tm_wday + 371) % 7 + 1;
333:
334: /* and subtract enough days */
335: day = cumdays[month + 1] - cumdays[month] +
336: (v1 + 1) * 7 - (v2 - day + 7) % 7;
337: #if DEBUG
338: fprintf(stderr, "\nMonth %d ends on weekday %d\n", month, v2);
339: #endif
1.1 millert 340: }
341:
342: /* first, second ... +1 ... +5 */
343: else {
1.5 pjanzen 344: v1 = day/10; /* offset */
1.1 millert 345: day = day % 10;
346:
1.5 pjanzen 347: /* which weekday the first of the month is */
348: v2 = (cumdays[month] - tp->tm_yday +
349: tp->tm_wday + 372) % 7 + 1;
350:
351: /* and add enough days */
352: day = 1 + (v1 - 1) * 7 + (day - v2 + 7) % 7;
353: #if DEBUG
354: fprintf(stderr, "\nMonth %d starts on weekday %d\n", month, v2);
355: #endif
1.1 millert 356: }
357: }
1.6 ! pjanzen 358: else
! 359: day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
1.1 millert 360: }
361:
362: if (!(flags & F_EASTER)) {
1.6 ! pjanzen 363: monthp = month;
! 364: dayp = day;
1.1 millert 365: day = cumdays[month] + day;
366: }
367: else {
368: for (v1 = 0; day > cumdays[v1]; v1++)
369: ;
1.6 ! pjanzen 370: monthp = v1 - 1;
! 371: dayp = day - cumdays[v1 - 1];
! 372: varp = 1;
1.1 millert 373: }
374:
375: #if DEBUG
1.6 ! pjanzen 376: fprintf(stderr, "day2: day %d(%d) yday %d\n", dayp, day, tp->tm_yday);
1.1 millert 377: #endif
378: /* if today or today + offset days */
1.6 ! pjanzen 379: if ((day >= tp->tm_yday - f_dayBefore &&
! 380: day <= tp->tm_yday + offset + f_dayAfter) ||
1.1 millert 381:
382: /* if number of days left in this year + days to event in next year */
1.6 ! pjanzen 383: (yrdays - tp->tm_yday + day <= offset + f_dayAfter ||
1.1 millert 384: /* a year backward, eg. 6 Jan and 10 days before -> 27. Dec */
385: tp->tm_yday + day - f_dayBefore < 0
1.6 ! pjanzen 386: )) {
! 387: if ((matches = malloc(sizeof(struct match))) == NULL)
! 388: errx(1,"cannot allocate memory");
! 389: matches->month = monthp;
! 390: matches->day = dayp;
! 391: matches->var = varp;
! 392: matches->year = tp->tm_year; /* XXX */
! 393: matches->next = NULL;
! 394: return (matches);
! 395: }
! 396: return (NULL);
1.1 millert 397: }
398:
399:
400: int
401: getmonth(s)
402: register char *s;
403: {
404: register char **p;
405: struct fixs *n;
406:
407: for (n = fnmonths; n->name; ++n)
408: if (!strncasecmp(s, n->name, n->len))
409: return ((n - fnmonths) + 1);
410: for (n = nmonths; n->name; ++n)
411: if (!strncasecmp(s, n->name, n->len))
412: return ((n - nmonths) + 1);
413: for (p = months; *p; ++p)
414: if (!strncasecmp(s, *p, 3))
415: return ((p - months) + 1);
416: return (0);
417: }
418:
419:
420: int
421: getday(s)
422: register char *s;
423: {
424: register char **p;
425: struct fixs *n;
426:
427: for (n = fndays; n->name; ++n)
428: if (!strncasecmp(s, n->name, n->len))
429: return ((n - fndays) + 1);
430: for (n = ndays; n->name; ++n)
431: if (!strncasecmp(s, n->name, n->len))
432: return ((n - ndays) + 1);
433: for (p = days; *p; ++p)
434: if (!strncasecmp(s, *p, 3))
435: return ((p - days) + 1);
436: return (0);
437: }
438:
439: /* return offset for variable weekdays
440: * -1 -> last weekday in month
441: * +1 -> first weekday in month
442: * ... etc ...
443: */
444: int
445: getdayvar(s)
446: register char *s;
447: {
448: register int offset;
449:
450:
451: offset = strlen(s);
452:
453:
454: /* Sun+1 or Wednesday-2
455: * ^ ^ */
456:
457: /* printf ("x: %s %s %d\n", s, s + offset - 2, offset); */
458: switch(*(s + offset - 2)) {
459: case '-':
460: return(-(atoi(s + offset - 1)));
461: break;
462: case '+':
463: return(atoi(s + offset - 1));
464: break;
465: }
466:
467:
468: /*
469: * some aliases: last, first, second, third, fourth
470: */
471:
472: /* last */
473: if (offset > 4 && !strcasecmp(s + offset - 4, "last"))
474: return(-1);
475: else if (offset > 5 && !strcasecmp(s + offset - 5, "first"))
476: return(+1);
477: else if (offset > 6 && !strcasecmp(s + offset - 6, "second"))
478: return(+2);
479: else if (offset > 5 && !strcasecmp(s + offset - 5, "third"))
480: return(+3);
481: else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth"))
482: return(+4);
483:
484:
485: /* no offset detected */
486: return(0);
487: }