Annotation of src/usr.bin/calendar/day.c, Revision 1.14
1.14 ! mpech 1: /* $OpenBSD: day.c,v 1.13 2001/09/27 18:19:20 mickey 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.14 ! mpech 46: static char rcsid[] = "$OpenBSD: day.c,v 1.13 2001/09/27 18:19:20 mickey 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:
1.7 pjanzen 65: #define WEEKLY 1
66: #define MONTHLY 2
67: #define YEARLY 3
68:
1.1 millert 69: struct tm *tp;
1.7 pjanzen 70: int *cumdays, offset;
1.1 millert 71: char dayname[10];
72:
73:
74: /* 1-based month, 0-based days, cumulative */
75: int daytab[][14] = {
76: { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 },
77: { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
78: };
79:
80: static char *days[] = {
81: "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
82: };
83:
84: static char *months[] = {
85: "jan", "feb", "mar", "apr", "may", "jun",
86: "jul", "aug", "sep", "oct", "nov", "dec", NULL,
87: };
88:
89: static struct fixs fndays[8]; /* full national days names */
90: static struct fixs ndays[8]; /* short national days names */
91:
92: static struct fixs fnmonths[13]; /* full national months names */
93: static struct fixs nmonths[13]; /* short national month names */
94:
95:
96: void setnnames(void)
97: {
98: char buf[80];
99: int i, l;
100: struct tm tm;
101:
102: for (i = 0; i < 7; i++) {
103: tm.tm_wday = i;
1.3 millert 104: l = strftime(buf, sizeof(buf), "%a", &tm);
105: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 106: ;
107: buf[l] = '\0';
108: if (ndays[i].name != NULL)
109: free(ndays[i].name);
110: if ((ndays[i].name = strdup(buf)) == NULL)
1.11 pjanzen 111: err(1, NULL);
1.1 millert 112: ndays[i].len = strlen(buf);
113:
1.3 millert 114: l = strftime(buf, sizeof(buf), "%A", &tm);
115: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 116: ;
117: buf[l] = '\0';
118: if (fndays[i].name != NULL)
119: free(fndays[i].name);
120: if ((fndays[i].name = strdup(buf)) == NULL)
1.11 pjanzen 121: err(1, NULL);
1.1 millert 122: fndays[i].len = strlen(buf);
123: }
124:
125: for (i = 0; i < 12; i++) {
126: tm.tm_mon = i;
1.3 millert 127: l = strftime(buf, sizeof(buf), "%b", &tm);
128: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 129: ;
130: buf[l] = '\0';
131: if (nmonths[i].name != NULL)
132: free(nmonths[i].name);
133: if ((nmonths[i].name = strdup(buf)) == NULL)
1.11 pjanzen 134: err(1, NULL);
1.1 millert 135: nmonths[i].len = strlen(buf);
136:
1.3 millert 137: l = strftime(buf, sizeof(buf), "%B", &tm);
138: for (; l > 0 && isspace((int)buf[l - 1]); l--)
1.1 millert 139: ;
140: buf[l] = '\0';
141: if (fnmonths[i].name != NULL)
142: free(fnmonths[i].name);
143: if ((fnmonths[i].name = strdup(buf)) == NULL)
1.11 pjanzen 144: err(1, NULL);
1.1 millert 145: fnmonths[i].len = strlen(buf);
146: }
1.7 pjanzen 147: /* Hardwired special events */
148: spev[0].name = strdup(EASTER);
149: spev[0].nlen = EASTERNAMELEN;
150: spev[0].getev = easter;
151: spev[1].name = strdup(PASKHA);
152: spev[1].nlen = PASKHALEN;
153: spev[1].getev = paskha;
154: for (i = 0; i < NUMEV; i++) {
155: if (spev[i].name == NULL)
1.11 pjanzen 156: err(1, NULL);
1.7 pjanzen 157: spev[i].uname = NULL;
158: }
1.1 millert 159: }
160:
161: void
162: settime(now)
1.7 pjanzen 163: time_t *now;
1.1 millert 164: {
1.7 pjanzen 165: tp = localtime(now);
166: tp->tm_sec = 0;
167: tp->tm_min = 0;
168: /* Avoid getting caught by a timezone shift; set time to noon */
169: tp->tm_isdst = 0;
170: tp->tm_hour = 12;
171: *now = mktime(tp);
172: if (isleap(tp->tm_year + TM_YEAR_BASE))
1.1 millert 173: cumdays = daytab[1];
1.7 pjanzen 174: else
1.1 millert 175: cumdays = daytab[0];
176: /* Friday displays Monday's events */
177: offset = tp->tm_wday == 5 ? 3 : 1;
1.7 pjanzen 178: if (f_dayAfter)
179: offset = 0; /* Except not when range is set explicitly */
1.1 millert 180: header[5].iov_base = dayname;
181:
182: (void) setlocale(LC_TIME, "C");
183: header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
184: (void) setlocale(LC_TIME, "");
185:
186: setnnames();
187: }
188:
1.2 millert 189: /* convert [Year][Month]Day into unix time (since 1970)
190: * Year: two or four digits, Month: two digits, Day: two digits
1.1 millert 191: */
192: time_t Mktime (date)
193: char *date;
194: {
195: time_t t;
196: int len;
197: struct tm tm;
198:
199: (void)time(&t);
200: tp = localtime(&t);
201:
202: len = strlen(date);
1.2 millert 203: if (len < 2)
204: return((time_t)-1);
1.1 millert 205: tm.tm_sec = 0;
206: tm.tm_min = 0;
1.5 pjanzen 207: /* Avoid getting caught by a timezone shift; set time to noon */
208: tm.tm_isdst = 0;
209: tm.tm_hour = 12;
1.1 millert 210: tm.tm_wday = 0;
211: tm.tm_mday = tp->tm_mday;
212: tm.tm_mon = tp->tm_mon;
213: tm.tm_year = tp->tm_year;
214:
1.2 millert 215: /* Day */
216: tm.tm_mday = atoi(date + len - 2);
1.1 millert 217:
1.2 millert 218: /* Month */
1.1 millert 219: if (len >= 4) {
1.2 millert 220: *(date + len - 2) = '\0';
221: tm.tm_mon = atoi(date + len - 4) - 1;
1.1 millert 222: }
223:
224: /* Year */
1.5 pjanzen 225: if (len >= 6) {
1.7 pjanzen 226: *(date + len - 4) = '\0';
227: tm.tm_year = atoi(date);
1.1 millert 228:
1.4 deraadt 229: /* tm_year up TM_YEAR_BASE ... */
1.5 pjanzen 230: if (tm.tm_year < 69) /* Y2K */
1.4 deraadt 231: tm.tm_year += 2000 - TM_YEAR_BASE;
232: else if (tm.tm_year < 100)
233: tm.tm_year += 1900 - TM_YEAR_BASE;
234: else if (tm.tm_year > TM_YEAR_BASE)
235: tm.tm_year -= TM_YEAR_BASE;
1.1 millert 236: }
237:
238: #if DEBUG
239: printf("Mktime: %d %d %d %s\n", (int)mktime(&tm), (int)t, len,
240: asctime(&tm));
241: #endif
242: return(mktime(&tm));
243: }
244:
245: /*
246: * Possible date formats include any combination of:
247: * 3-charmonth (January, Jan, Jan)
248: * 3-charweekday (Friday, Monday, mon.)
249: * numeric month or day (1, 2, 04)
250: *
1.10 pjanzen 251: * Any character except \t or '*' may separate them, or they may not be
252: * separated. Any line following a line that is matched, that starts
253: * with \t, is shown along with the matched line.
1.1 millert 254: */
1.6 pjanzen 255: struct match *
1.12 mickey 256: isnow(endp, bodun)
1.1 millert 257: char *endp;
1.12 mickey 258: int bodun;
1.1 millert 259: {
1.7 pjanzen 260: int day = 0, flags = 0, month = 0, v1, v2, i;
261: int monthp, dayp, varp = 0;
262: struct match *matches = NULL, *tmp, *tmp2;
263: int interval = YEARLY; /* how frequently the event repeats. */
264: int vwd = 0; /* Variable weekday */
265: time_t tdiff, ttmp;
266: struct tm tmtmp;
1.1 millert 267:
268: /*
269: * CONVENTION
270: *
271: * Month: 1-12
272: * Monthname: Jan .. Dec
273: * Day: 1-31
274: * Weekday: Mon-Sun
275: *
276: */
277:
278: /* read first field */
279: /* didn't recognize anything, skip it */
280: if (!(v1 = getfield(endp, &endp, &flags)))
1.6 pjanzen 281: return (NULL);
1.1 millert 282:
1.12 mickey 283: /* adjust bodun rate */
284: if (bodun && !bodun_always)
285: bodun = !(arc4random() % 3);
286:
1.1 millert 287: /* Easter or Easter depending days */
1.7 pjanzen 288: if (flags & F_SPECIAL)
289: vwd = v1;
1.1 millert 290:
291: /*
292: * 1. {Weekday,Day} XYZ ...
293: *
294: * where Day is > 12
295: */
296: else if (flags & F_ISDAY || v1 > 12) {
297:
1.7 pjanzen 298: /* found a day; day: 13-31 or weekday: 1-7 */
1.1 millert 299: day = v1;
300:
301: /* {Day,Weekday} {Month,Monthname} ... */
1.7 pjanzen 302: /* if no recognizable month, assume just a day alone -- this is
303: * very unlikely and can only happen after the first 12 days.
304: * --find month or use current month */
305: if (!(month = getfield(endp, &endp, &flags))) {
1.1 millert 306: month = tp->tm_mon + 1;
1.7 pjanzen 307: /* F_ISDAY is set only if a weekday was spelled out */
308: /* F_ISDAY must be set if 0 < day < 8 */
309: if ((day <= 7) && (day >= 1))
310: interval = WEEKLY;
311: else
312: interval = MONTHLY;
313: } else if ((day <= 7) && (day >= 1))
314: day += 10;
315: /* it's a weekday; make it the first one of the month */
316: if (month == -1) {
317: month = tp->tm_mon + 1;
318: interval = MONTHLY;
319: }
320: if ((month > 12) || (month < 1))
321: return (NULL);
1.1 millert 322: }
323:
324: /* 2. {Monthname} XYZ ... */
325: else if (flags & F_ISMONTH) {
326: month = v1;
1.7 pjanzen 327: if (month == -1) {
328: month = tp->tm_mon + 1;
329: interval = MONTHLY;
330: }
1.1 millert 331: /* Monthname {day,weekday} */
332: /* if no recognizable day, assume the first day in month */
333: if (!(day = getfield(endp, &endp, &flags)))
334: day = 1;
1.7 pjanzen 335: /* If a weekday was spelled out without an ordering,
336: * assume the first of that day in the month */
337: if ((flags & F_ISDAY) && (day >= 1) && (day <=7))
338: day += 10;
1.1 millert 339: }
340:
341: /* Hm ... */
342: else {
343: v2 = getfield(endp, &endp, &flags);
344:
345: /*
346: * {Day} {Monthname} ...
347: * where Day <= 12
348: */
349: if (flags & F_ISMONTH) {
350: day = v1;
351: month = v2;
1.7 pjanzen 352: if (month == -1) {
353: month = tp->tm_mon + 1;
354: interval = MONTHLY;
355: }
1.1 millert 356: }
357:
358: /* {Month} {Weekday,Day} ... */
359: else {
360: /* F_ISDAY set, v2 > 12, or no way to tell */
361: month = v1;
362: /* if no recognizable day, assume the first */
363: day = v2 ? v2 : 1;
1.7 pjanzen 364: if ((flags & F_ISDAY) && (day >= 1) && (day <= 7))
365: day += 10;
1.1 millert 366: }
367: }
368:
369: /* convert Weekday into *next* Day,
370: * e.g.: 'Sunday' -> 22
1.5 pjanzen 371: * 'SundayLast' -> ??
1.1 millert 372: */
373: if (flags & F_ISDAY) {
374: #if DEBUG
1.7 pjanzen 375: fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month);
1.1 millert 376: #endif
377:
1.7 pjanzen 378: varp = 1;
379: /* variable weekday, SundayLast, MondayFirst ... */
380: if (day < 0 || day >= 10)
381: vwd = day;
382: else {
383: day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
384: interval = WEEKLY;
385: }
386: } else
387: /* Check for silliness. Note we still catch Feb 29 */
388: if (!(flags & F_SPECIAL) &&
389: (day > (cumdays[month + 1] - cumdays[month]) || day < 1)) {
390: if (!((month == 2 && day == 29) ||
391: (interval == MONTHLY && day <= 31)))
392: return (NULL);
1.1 millert 393: }
394:
1.7 pjanzen 395: if (!(flags & F_SPECIAL)) {
396: monthp = month;
397: dayp = day;
398: day = cumdays[month] + day;
1.5 pjanzen 399: #if DEBUG
1.7 pjanzen 400: fprintf(stderr, "day2: day %d(%d) yday %d\n", dayp, day, tp->tm_yday);
1.5 pjanzen 401: #endif
1.7 pjanzen 402: /* Speed up processing for the most common situation: yearly events
403: * when the interval being checked is less than a month or so (this
404: * could be less than a year, but then we have to start worrying about
405: * leap years). Only one event can match, and it's easy to find.
406: * Note we can't check special events, because they can wander widely.
407: */
408: if (((v1 = offset + f_dayAfter) < 50) && (interval == YEARLY)) {
409: memcpy(&tmtmp, tp, sizeof(struct tm));
410: tmtmp.tm_mday = dayp;
411: tmtmp.tm_mon = monthp - 1;
1.8 pjanzen 412: if (vwd) {
413: /* We want the event next year if it's late now
414: * this year. The 50-day limit means we don't have to
415: * worry if next year is or isn't a leap year.
416: */
417: if (tp->tm_yday > 300 && tmtmp.tm_mon <= 1)
418: variable_weekday(&vwd, tmtmp.tm_mon + 1,
419: tmtmp.tm_year + TM_YEAR_BASE + 1);
420: else
421: variable_weekday(&vwd, tmtmp.tm_mon + 1,
422: tmtmp.tm_year + TM_YEAR_BASE);
423: day = cumdays[tmtmp.tm_mon + 1] + vwd;
424: tmtmp.tm_mday = vwd;
425: }
1.7 pjanzen 426: v2 = day - tp->tm_yday;
427: if ((v2 > v1) || (v2 < 0)) {
428: if ((v2 += isleap(tp->tm_year + TM_YEAR_BASE) ? 366 : 365)
429: <= v1)
430: tmtmp.tm_year++;
1.12 mickey 431: else if(!bodun || (day - tp->tm_yday) != -1)
1.7 pjanzen 432: return(NULL);
433: }
434: if ((tmp = malloc(sizeof(struct match))) == NULL)
1.11 pjanzen 435: err(1, NULL);
1.13 mickey 436:
437: if (bodun && (day - tp->tm_yday) == -1) {
438: tmp->when = f_time - 1 * SECSPERDAY;
439: tmtmp.tm_mday++;
440: tmp->bodun = 1;
441: } else {
442: tmp->when = f_time + v2 * SECSPERDAY;
443: tmp->bodun = 0;
444: }
445:
1.7 pjanzen 446: (void)mktime(&tmtmp);
447: if (strftime(tmp->print_date,
448: sizeof(tmp->print_date),
449: /* "%a %b %d", &tm); Skip weekdays */
450: "%b %d", &tmtmp) == 0)
451: tmp->print_date[sizeof(tmp->print_date) - 1] = '\0';
1.12 mickey 452:
1.7 pjanzen 453: tmp->var = varp;
454: tmp->next = NULL;
455: return(tmp);
456: }
1.1 millert 457: }
458: else {
1.7 pjanzen 459: varp = 1;
460: /* Set up v1 to the event number and ... */
461: v1 = vwd % (NUMEV + 1) - 1;
462: vwd /= (NUMEV + 1);
463: if (v1 < 0) {
464: v1 += NUMEV + 1;
465: vwd--;
466: }
467: dayp = monthp = 1; /* Why not */
1.1 millert 468: }
469:
1.7 pjanzen 470: /* Compare to past and coming instances of the event. The i == 0 part
471: * of the loop corresponds to this specific instance. Note that we
472: * can leave things sort of higgledy-piggledy since a mktime() happens
473: * on this before anything gets printed. Also note that even though
474: * we've effectively gotten rid of f_dayBefore, we still have to check
475: * the one prior event for situations like "the 31st of every month"
476: * and "yearly" events which could happen twice in one year but not in
477: * the next */
478: tmp2 = matches;
479: for (i = -1; i < 2; i++) {
480: memcpy(&tmtmp, tp, sizeof(struct tm));
481: tmtmp.tm_mday = dayp;
482: tmtmp.tm_mon = month = monthp - 1;
483: do {
484: v2 = 0;
485: switch (interval) {
486: case WEEKLY:
487: tmtmp.tm_mday += 7 * i;
488: break;
489: case MONTHLY:
490: month += i;
491: tmtmp.tm_mon = month;
492: switch(tmtmp.tm_mon) {
493: case -1:
494: tmtmp.tm_mon = month = 11;
495: tmtmp.tm_year--;
496: break;
497: case 12:
498: tmtmp.tm_mon = month = 0;
499: tmtmp.tm_year++;
500: break;
501: }
502: if (vwd) {
503: v1 = vwd;
504: variable_weekday(&v1, tmtmp.tm_mon + 1,
505: tmtmp.tm_year + TM_YEAR_BASE);
506: tmtmp.tm_mday = v1;
507: } else
508: tmtmp.tm_mday = dayp;
509: break;
510: case YEARLY:
511: default:
512: tmtmp.tm_year += i;
513: if (flags & F_SPECIAL) {
514: tmtmp.tm_mon = 0; /* Gee, mktime() is nice */
515: tmtmp.tm_mday = spev[v1].getev(tmtmp.tm_year +
1.9 pjanzen 516: TM_YEAR_BASE) + vwd;
1.7 pjanzen 517: } else if (vwd) {
518: v1 = vwd;
519: variable_weekday(&v1, tmtmp.tm_mon + 1,
520: tmtmp.tm_year + TM_YEAR_BASE);
521: tmtmp.tm_mday = v1;
522: } else {
523: /* Need the following to keep Feb 29 from
524: * becoming Mar 1 */
525: tmtmp.tm_mday = dayp;
526: tmtmp.tm_mon = monthp - 1;
527: }
528: break;
529: }
530: /* How many days apart are we */
531: if ((ttmp = mktime(&tmtmp)) == -1)
532: warnx("time out of range: %s", endp);
533: else {
534: tdiff = difftime(ttmp, f_time)/ SECSPERDAY;
1.12 mickey 535: if (tdiff <= offset + f_dayAfter ||
536: (bodun && tdiff == -1)) {
537: if (tdiff >= 0 ||
538: (bodun && tdiff == -1)) {
1.7 pjanzen 539: if ((tmp = malloc(sizeof(struct match))) == NULL)
1.11 pjanzen 540: err(1, NULL);
1.7 pjanzen 541: tmp->when = ttmp;
542: if (strftime(tmp->print_date,
543: sizeof(tmp->print_date),
544: /* "%a %b %d", &tm); Skip weekdays */
545: "%b %d", &tmtmp) == 0)
546: tmp->print_date[sizeof(tmp->print_date) - 1] = '\0';
1.13 mickey 547: tmp->bodun = bodun && tdiff == -1;
1.7 pjanzen 548: tmp->var = varp;
549: tmp->next = NULL;
550: if (tmp2)
551: tmp2->next = tmp;
552: else
553: matches = tmp;
554: tmp2 = tmp;
555: v2 = (i == 1) ? 1 : 0;
556: }
557: } else
558: i = 2; /* No point checking in the future */
559: }
560: } while (v2 != 0);
1.6 pjanzen 561: }
1.7 pjanzen 562: return (matches);
1.1 millert 563: }
564:
565:
566: int
567: getmonth(s)
1.14 ! mpech 568: char *s;
1.1 millert 569: {
1.14 ! mpech 570: char **p;
1.1 millert 571: struct fixs *n;
572:
573: for (n = fnmonths; n->name; ++n)
574: if (!strncasecmp(s, n->name, n->len))
575: return ((n - fnmonths) + 1);
576: for (n = nmonths; n->name; ++n)
577: if (!strncasecmp(s, n->name, n->len))
578: return ((n - nmonths) + 1);
579: for (p = months; *p; ++p)
580: if (!strncasecmp(s, *p, 3))
581: return ((p - months) + 1);
582: return (0);
583: }
584:
585:
586: int
587: getday(s)
1.14 ! mpech 588: char *s;
1.1 millert 589: {
1.14 ! mpech 590: char **p;
1.1 millert 591: struct fixs *n;
592:
593: for (n = fndays; n->name; ++n)
594: if (!strncasecmp(s, n->name, n->len))
595: return ((n - fndays) + 1);
596: for (n = ndays; n->name; ++n)
597: if (!strncasecmp(s, n->name, n->len))
598: return ((n - ndays) + 1);
599: for (p = days; *p; ++p)
600: if (!strncasecmp(s, *p, 3))
601: return ((p - days) + 1);
602: return (0);
603: }
604:
605: /* return offset for variable weekdays
606: * -1 -> last weekday in month
607: * +1 -> first weekday in month
608: * ... etc ...
609: */
610: int
611: getdayvar(s)
1.14 ! mpech 612: char *s;
1.1 millert 613: {
1.14 ! mpech 614: int offset;
1.1 millert 615:
616:
617: offset = strlen(s);
618:
619: /* Sun+1 or Wednesday-2
620: * ^ ^ */
621:
622: /* printf ("x: %s %s %d\n", s, s + offset - 2, offset); */
623: switch(*(s + offset - 2)) {
624: case '-':
625: case '+':
1.7 pjanzen 626: return(atoi(s + offset - 2));
1.1 millert 627: break;
628: }
629:
630: /*
631: * some aliases: last, first, second, third, fourth
632: */
633:
634: /* last */
635: if (offset > 4 && !strcasecmp(s + offset - 4, "last"))
636: return(-1);
637: else if (offset > 5 && !strcasecmp(s + offset - 5, "first"))
638: return(+1);
639: else if (offset > 6 && !strcasecmp(s + offset - 6, "second"))
640: return(+2);
641: else if (offset > 5 && !strcasecmp(s + offset - 5, "third"))
642: return(+3);
643: else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth"))
644: return(+4);
645:
646: /* no offset detected */
647: return(0);
1.7 pjanzen 648: }
649:
650:
651: int
652: foy(year)
653: int year;
654: {
655: /* 0-6; what weekday Jan 1 is */
656: year--;
657: return ((1 - year/100 + year/400 + (int)(365.25 * year)) % 7);
658: }
659:
660:
661:
662: void
663: variable_weekday(day, month, year)
664: int *day, month, year;
665: {
666: int v1, v2;
667: int *cumdays;
668: int day1;
669:
670: if (isleap(year))
671: cumdays = daytab[1];
672: else
673: cumdays = daytab[0];
674: day1 = foy(year);
675: /* negative offset; last, -4 .. -1 */
676: if (*day < 0) {
677: v1 = *day/10 - 1; /* offset -4 ... -1 */
678: *day = 10 + (*day % 10); /* day 1 ... 7 */
679:
680: /* which weekday the end of the month is (1-7) */
681: v2 = (cumdays[month + 1] + day1) % 7 + 1;
682:
683: /* and subtract enough days */
684: *day = cumdays[month + 1] - cumdays[month] +
685: (v1 + 1) * 7 - (v2 - *day + 7) % 7;
686: #if DEBUG
687: fprintf(stderr, "\nMonth %d ends on weekday %d\n", month, v2);
688: #endif
689: }
690:
691: /* first, second ... +1 ... +5 */
692: else {
693: v1 = *day/10; /* offset */
694: *day = *day % 10;
695:
696: /* which weekday the first of the month is (1-7) */
697: v2 = (cumdays[month] + 1 + day1) % 7 + 1;
698:
699: /* and add enough days */
700: *day = 1 + (v1 - 1) * 7 + (*day - v2 + 7) % 7;
701: #if DEBUG
702: fprintf(stderr, "\nMonth %d starts on weekday %d\n", month, v2);
703: #endif
704: }
1.1 millert 705: }