GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/calendar/day.c Lines: 274 318 86.2 %
Date: 2017-11-07 Branches: 160 227 70.5 %

Line Branch Exec Source
1
/*	$OpenBSD: day.c,v 1.34 2016/09/14 15:09:46 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
712728
	if (strftime(m->print_date, sizeof(m->print_date),
84
356364
	    daynames ? "%a %b %d" : "%b %d", tm) == 0)
85
		m->print_date[sizeof(m->print_date) - 1] = '\0';
86
178182
}
87
88
void
89
setnnames(void)
90
{
91
13160
	char buf[80];
92
	int i, l;
93
6580
	struct tm tm;
94
95
105280
	for (i = 0; i < 7; i++) {
96
46060
		tm.tm_wday = i;
97
46060
		l = strftime(buf, sizeof(buf), "%a", &tm);
98

138180
		for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
99
			;
100
46060
		buf[l] = '\0';
101
46060
		free(ndays[i].name);
102
46060
		if ((ndays[i].name = strdup(buf)) == NULL)
103
			err(1, NULL);
104
46060
		ndays[i].len = strlen(buf);
105
106
46060
		l = strftime(buf, sizeof(buf), "%A", &tm);
107

138180
		for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
108
			;
109
46060
		buf[l] = '\0';
110
46060
		free(fndays[i].name);
111
46060
		if ((fndays[i].name = strdup(buf)) == NULL)
112
			err(1, NULL);
113
46060
		fndays[i].len = strlen(buf);
114
	}
115
116
171080
	for (i = 0; i < 12; i++) {
117
78960
		tm.tm_mon = i;
118
78960
		l = strftime(buf, sizeof(buf), "%b", &tm);
119

236880
		for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
120
			;
121
78960
		buf[l] = '\0';
122
78960
		free(nmonths[i].name);
123
78960
		if ((nmonths[i].name = strdup(buf)) == NULL)
124
			err(1, NULL);
125
78960
		nmonths[i].len = strlen(buf);
126
127
78960
		l = strftime(buf, sizeof(buf), "%B", &tm);
128

236880
		for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
129
			;
130
78960
		buf[l] = '\0';
131
78960
		free(fnmonths[i].name);
132
78960
		if ((fnmonths[i].name = strdup(buf)) == NULL)
133
			err(1, NULL);
134
78960
		fnmonths[i].len = strlen(buf);
135
	}
136
	/* Hardwired special events */
137
6580
	spev[0].name = strdup(PESACH);
138
6580
	spev[0].nlen = PESACHLEN;
139
6580
	spev[0].getev = pesach;
140
6580
	spev[1].name = strdup(EASTER);
141
6580
	spev[1].nlen = EASTERNAMELEN;
142
6580
	spev[1].getev = easter;
143
6580
	spev[2].name = strdup(PASKHA);
144
6580
	spev[2].nlen = PASKHALEN;
145
6580
	spev[2].getev = paskha;
146
52640
	for (i = 0; i < NUMEV; i++) {
147
19740
		if (spev[i].name == NULL)
148
			err(1, NULL);
149
19740
		spev[i].uname = NULL;
150
	}
151
6580
}
152
153
void
154
settime(time_t *now)
155
{
156
13160
	tp = localtime(now);
157
6580
	tp->tm_sec = 0;
158
6580
	tp->tm_min = 0;
159
	/* Avoid getting caught by a timezone shift; set time to noon */
160
6580
	tp->tm_isdst = 0;
161
6580
	tp->tm_hour = 12;
162
6580
	*now = mktime(tp);
163

9874
	if (isleap(tp->tm_year + 1900))
164
3294
		cumdays = daytab[1];
165
	else
166
		cumdays = daytab[0];
167
	/* Friday displays Monday's events */
168
6580
	offset = tp->tm_wday == 5 ? 3 : 1;
169
6580
	if (f_SetdayAfter)
170
6580
		offset = 0;	/* Except not when range is set explicitly */
171
6580
	header[5].iov_base = dayname;
172
173
6580
	(void) setlocale(LC_TIME, "C");
174
6580
	header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
175
6580
	(void) setlocale(LC_TIME, "");
176
177
6580
	setnnames();
178
6580
}
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
13158
	time_t t;
187
	int len;
188
6579
	struct tm tm;
189
190
6579
	(void)time(&t);
191
6579
	tp = localtime(&t);
192
193
6579
	len = strlen(date);
194
6579
	if (len < 2)
195
		return((time_t)-1);
196
6579
	bzero(&tm, sizeof tm);
197
6579
	tm.tm_sec = 0;
198
6579
	tm.tm_min = 0;
199
	/* Avoid getting caught by a timezone shift; set time to noon */
200
6579
	tm.tm_isdst = 0;
201
6579
	tm.tm_hour = 12;
202
6579
	tm.tm_wday = 0;
203
6579
	tm.tm_mday = tp->tm_mday;
204
6579
	tm.tm_mon = tp->tm_mon;
205
6579
	tm.tm_year = tp->tm_year;
206
207
	/* Day */
208
6579
	tm.tm_mday = atoi(date + len - 2);
209
210
	/* Month */
211
6579
	if (len >= 4) {
212
6579
		*(date + len - 2) = '\0';
213
6579
		tm.tm_mon = atoi(date + len - 4) - 1;
214
6579
	}
215
216
	/* Year */
217
6579
	if (len >= 6) {
218
6579
		*(date + len - 4) = '\0';
219
6579
		tm.tm_year = atoi(date);
220
221
6579
		if (tm.tm_year < 69)		/* Y2K */
222
			tm.tm_year += 100;
223
6579
		else if (tm.tm_year > 1900)
224
6579
			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
6579
	return(mktime(&tm));
232
6579
}
233
234
static void
235
adjust_calendar(int *day, int *month)
236
{
237
4013190
	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
2006595
}
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
15493545
	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
15493545
	int vwd = 0;	/* Variable weekday */
272
	time_t tdiff, ttmp;
273
15493545
	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
15493545
	if (!(v1 = getfield(endp, &endp, &flags)))
288
		return (NULL);
289
290
	/* adjust bodun rate */
291
15493545
	if (bodun && !bodun_always)
292
		bodun = !arc4random_uniform(3);
293
294
	/* Easter or Easter depending days */
295
15493545
	if (flags & F_SPECIAL)
296
19737
		vwd = v1;
297
298
	 /*
299
	  * 1. {Weekday,Day} XYZ ...
300
	  *
301
	  *    where Day is > 12
302
	  */
303
15473808
	else if (flags & F_ISDAY || v1 > 12) {
304
305
		/* found a day; day: 13-31 or weekday: 1-7 */
306
2217123
		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
2217123
		if (!(month = getfield(endp, &endp, &flags))) {
313
875007
			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
875007
			if ((day <= 7) && (day >= 1))
317
46053
				interval = WEEKLY;
318
			else
319
				interval = MONTHLY;
320
1342116
		} else if ((day <= 7) && (day >= 1))
321
			day += 10;
322
			/* it's a weekday; make it the first one of the month */
323
2217123
		if (month == -1) {
324
125001
			month = tp->tm_mon + 1;
325
			interval = MONTHLY;
326
2217123
		} else if (calendar)
327
			adjust_calendar(&day, &month);
328
2217123
		if ((month > 12) || (month < 1))
329
			return (NULL);
330
	}
331
332
	/* 2. {Monthname} XYZ ... */
333
13256685
	else if (flags & F_ISMONTH) {
334
4855302
		month = v1;
335
4855302
		if (month == -1) {
336
6579
			month = tp->tm_mon + 1;
337
			interval = MONTHLY;
338
6579
		}
339
		/* Monthname {day,weekday} */
340
		/* if no recognizable day, assume the first day in month */
341
4855302
		if (!(day = getfield(endp, &endp, &flags)))
342
4855302
			day = 1;
343
		/* If a weekday was spelled out without an ordering,
344
		 * assume the first of that day in the month */
345
4855302
		if ((flags & F_ISDAY)) {
346
2763180
			if ((day >= 1) && (day <=7))
347
				day += 10;
348
2092122
		} else if (calendar)
349
			adjust_calendar(&day, &month);
350
	}
351
352
	/* Hm ... */
353
	else {
354
8401383
		v2 = getfield(endp, &endp, &flags);
355
356
		/*
357
		 * {Day} {Monthname} ...
358
		 * where Day <= 12
359
		 */
360
8401383
		if (flags & F_ISMONTH) {
361
868428
			day = v1;
362
868428
			month = v2;
363
868428
			if (month == -1) {
364
78948
				month = tp->tm_mon + 1;
365
				interval = MONTHLY;
366
868428
			} 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
7532955
			month = v1;
374
			/* if no recognizable day, assume the first */
375
7532955
			day = v2 ? v2 : 1;
376
7532955
			if ((flags & F_ISDAY)) {
377
5526360
				if ((day >= 1) && (day <= 7))
378
					day += 10;
379
			} else
380
2006595
				adjust_calendar(&day, &month);
381
		}
382
	}
383
384
	/* convert Weekday into *next*  Day,
385
	 * e.g.: 'Sunday' -> 22
386
	 *       'SundayLast' -> ??
387
	 */
388
15493545
	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
9164547
		if (day < 0 || day >= 10)
396
9118494
			vwd = day;
397
		else {
398
46053
			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
6328998
		if (!(flags & F_SPECIAL) &&
404
6309261
		    (day > (cumdays[month + 1] - cumdays[month]) || day < 1)) {
405
13293
			if (!((month == 2 && day == 29) ||
406
3186
			    (interval == MONTHLY && day <= 31)))
407
				return (NULL);
408
		}
409
410
15493545
	if (!(flags & F_SPECIAL)) {
411
15473808
		monthp = month;
412
15473808
		dayp = day;
413
15473808
		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
15473808
		if (((v1 = offset + f_dayAfter) < 50) && (interval == YEARLY)) {
424
14388273
			memcpy(&tmtmp, tp, sizeof(struct tm));
425
14388273
			tmtmp.tm_mday = dayp;
426
14388273
			tmtmp.tm_mon = monthp - 1;
427
14388273
			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

9752400
				if (tp->tm_yday > 300 && tmtmp.tm_mon <= 1)
433
243810
					variable_weekday(&vwd, tmtmp.tm_mon + 1,
434
243810
					    tmtmp.tm_year + 1900 + 1);
435
				else
436
16091460
					variable_weekday(&vwd, tmtmp.tm_mon + 1,
437
8045730
					    tmtmp.tm_year + 1900);
438
8289540
				day = cumdays[tmtmp.tm_mon + 1] + vwd;
439
8289540
				tmtmp.tm_mday = vwd;
440
8289540
			}
441
14388273
			v2 = day - tp->tm_yday;
442
14388273
			if ((v2 > v1) || (v2 < 0)) {
443

50055876
				if ((v2 += isleap(tp->tm_year + 1900) ? 366 : 365)
444
14298876
				    <= v1)
445
306
					tmtmp.tm_year++;
446

14298570
				else if(!bodun || (day - tp->tm_yday) != -1)
447
14298570
					return(NULL);
448
			}
449
89703
			if ((tmp = malloc(sizeof(struct match))) == NULL)
450
				err(1, NULL);
451
452

89703
			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
89703
				tmp->when = f_time + v2 * SECSPERDAY;
458
				tmp->bodun = 0;
459
			}
460
461
89703
			(void)mktime(&tmtmp);
462
89703
			fill_print_date(tmp, &tmtmp);
463
89703
			tmp->var   = varp;
464
89703
			tmp->next  = NULL;
465
89703
			return(tmp);
466
		}
467
	} else {
468
		varp = 1;
469
		/* Set up v1 to the event number and ... */
470
19737
		v1 = vwd % (NUMEV + 1) - 1;
471
19737
		vwd /= (NUMEV + 1);
472
19737
		if (v1 < 0) {
473
6579
			v1 += NUMEV + 1;
474
6579
			vwd--;
475
6579
		}
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
7753536
	for (i = -1; i < 2; i++) {
489
2771496
		memcpy(&tmtmp, tp, sizeof(struct tm));
490
2771496
		tmtmp.tm_mday = dayp;
491
2771496
		tmtmp.tm_mon = month = monthp - 1;
492
2771496
		do {
493
			v2 = 0;
494
2772891
			switch (interval) {
495
			case WEEKLY:
496
107154
				tmtmp.tm_mday += 7 * i;
497
107154
				break;
498
			case MONTHLY:
499
2745531
				month += i;
500
2745531
				tmtmp.tm_mon = month;
501
2745531
				switch(tmtmp.tm_mon) {
502
				case -1:
503
88164
					tmtmp.tm_mon = month = 11;
504
					tmtmp.tm_year--;
505
88164
					break;
506
				case 12:
507
45495
					tmtmp.tm_mon = month = 0;
508
					tmtmp.tm_year++;
509
45495
					break;
510
				}
511
2745531
				if (vwd) {
512
2071836
					v1 = vwd;
513
4143672
					variable_weekday(&v1, tmtmp.tm_mon + 1,
514
2071836
					    tmtmp.tm_year + 1900);
515
2071836
					tmtmp.tm_mday = v1;
516
2071836
				} else
517
					tmtmp.tm_mday = dayp;
518
2611872
				break;
519
			case YEARLY:
520
			default:
521
53865
				tmtmp.tm_year += i;
522
53865
				if (flags & F_SPECIAL) {
523
53865
					tmtmp.tm_mon = 0;	/* Gee, mktime() is nice */
524
107730
					tmtmp.tm_mday = spev[v1].getev(tmtmp.tm_year +
525
53865
					    1900) + vwd;
526
53865
				} 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
2772891
			if ((ttmp = mktime(&tmtmp)) == -1)
541
				warnx("time out of range: %s", endp);
542
			else {
543
2772891
				tdiff = difftime(ttmp, f_time)/ SECSPERDAY;
544
2772891
				if (tdiff <= offset + f_dayAfter ||
545
1105272
				    (bodun && tdiff == -1)) {
546
1667619
					if (((tmtmp.tm_mon == month) ||
547
206226
					     (flags & F_SPECIAL) ||
548
103113
					     (interval == WEEKLY)) &&
549
1605042
					    (tdiff >=  0 ||
550
1516563
					    (bodun && tdiff == -1))) {
551
88479
					if ((tmp = malloc(sizeof(struct match))) == NULL)
552
						err(1, NULL);
553
88479
					tmp->when = ttmp;
554
88479
					fill_print_date(tmp, &tmtmp);
555
88479
					tmp->bodun = bodun && tdiff == -1;
556
88479
					tmp->var   = varp;
557
88479
					tmp->next  = NULL;
558
88479
					if (tmp2)
559
						tmp2->next = tmp;
560
					else
561
						matches = tmp;
562
					tmp2 = tmp;
563
88479
					v2 = (i == 1) ? 1 : 0;
564
88479
					}
565
				} else
566
					i = 2; /* No point checking in the future */
567
			}
568
2772891
		} while (v2 != 0);
569
	}
570
1105272
	return (matches);
571
15493545
}
572
573
574
int
575
getmonth(char *s)
576
{
577
	char **p;
578
	struct fixs *n;
579
580
423411282
	for (n = fnmonths; n->name; ++n)
581
188725194
		if (!strncasecmp(s, n->name, n->len))
582
2519757
			return ((n - fnmonths) + 1);
583
319897296
	for (n = nmonths; n->name; ++n)
584
149803830
		if (!strncasecmp(s, n->name, n->len))
585
4335561
			return ((n - nmonths) + 1);
586
263765268
	for (p = months; *p; ++p)
587
121737816
		if (!strncasecmp(s, *p, 3))
588
			return ((p - months) + 1);
589
10144818
	return (0);
590
17000136
}
591
592
593
int
594
getday(char *s)
595
{
596
	char **p;
597
	struct fixs *n;
598
599
124566786
	for (n = fndays; n->name; ++n)
600
53053056
		if (!strncasecmp(s, n->name, n->len))
601
5986890
			return ((n - fndays) + 1);
602
41105592
	for (n = ndays; n->name; ++n)
603
19572525
		if (!strncasecmp(s, n->name, n->len))
604
3177657
			return ((n - ndays) + 1);
605
15684336
	for (p = days; *p; ++p)
606
6861897
		if (!strncasecmp(s, *p, 3))
607
			return ((p - days) + 1);
608
980271
	return (0);
609
10144818
}
610
611
/* return offset for variable weekdays
612
 * -1 -> last weekday in month
613
 * +1 -> first weekday in month
614
 * ... etc ...
615
 */
616
int
617
getdayvar(char *s)
618
{
619
	int offset;
620
621
622
18329094
	offset = strlen(s);
623
624
	/* Sun+1 or Wednesday-2
625
	 *    ^              ^   */
626
627
	/* printf ("x: %s %s %d\n", s, s + offset - 2, offset); */
628
9164547
	switch(*(s + offset - 2)) {
629
	case '-':
630
	case '+':
631
9118494
	    return(atoi(s + offset - 2));
632
	    break;
633
	}
634
635
	/*
636
	 * some aliases: last, first, second, third, fourth
637
	 */
638
639
	/* last */
640

92106
	if      (offset > 4 && !strcasecmp(s + offset - 4, "last"))
641
	    return(-1);
642

92106
	else if (offset > 5 && !strcasecmp(s + offset - 5, "first"))
643
	    return(+1);
644

72369
	else if (offset > 6 && !strcasecmp(s + offset - 6, "second"))
645
	    return(+2);
646

92106
	else if (offset > 5 && !strcasecmp(s + offset - 5, "third"))
647
	    return(+3);
648

72369
	else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth"))
649
	    return(+4);
650
651
	/* no offset detected */
652
46053
	return(0);
653
9164547
}
654
655
656
int
657
foy(int year)
658
{
659
	/* 0-6; what weekday Jan 1 is */
660
20722752
	year--;
661
10361376
	return ((1 - year/100 + year/400 + (int)(365.25 * year)) % 7);
662
}
663
664
665
666
void
667
variable_weekday(int *day, int month, int year)
668
{
669
	int v1, v2;
670
	int *cumdays;
671
	int day1;
672
673

25770060
	if (isleap(year))
674
5047308
		cumdays = daytab[1];
675
	else
676
		cumdays = daytab[0];
677
10361376
	day1 = foy(year);
678
	/* negative offset; last, -4 .. -1 */
679
10361376
	if (*day < 0) {
680
927792
		v1 = *day/10 - 1;          /* offset -4 ... -1 */
681
927792
		*day = 10 + (*day % 10);    /* day 1 ... 7 */
682
683
		/* which weekday the end of the month is (1-7) */
684
927792
		v2 = (cumdays[month + 1] + day1) % 7 + 1;
685
686
		/* and subtract enough days */
687
2783376
		*day = cumdays[month + 1] - cumdays[month] +
688
1855584
		    (v1 + 1) * 7 - (v2 - *day + 7) % 7;
689
#if DEBUG
690
		fprintf(stderr, "\nMonth %d ends on weekday %d\n", month, v2);
691
#endif
692
927792
	}
693
694
	/* first, second ... +1 ... +5 */
695
	else {
696
		v1 = *day/10;        /* offset */
697
9433584
		*day = *day % 10;
698
699
		/* which weekday the first of the month is (1-7) */
700
9433584
		v2 = (cumdays[month] + 1 + day1) % 7 + 1;
701
702
		/* and add enough days */
703
9433584
		*day = 1 + (v1 - 1) * 7 + (*day - v2 + 7) % 7;
704
#if DEBUG
705
		fprintf(stderr, "\nMonth %d starts on weekday %d\n", month, v2);
706
#endif
707
	}
708
10361376
}