GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/calendar/day.c Lines: 0 300 0.0 %
Date: 2016-12-06 Branches: 0 246 0.0 %

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