GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/calendar/day.c Lines: 272 316 86.1 %
Date: 2017-11-13 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
395960
	if (strftime(m->print_date, sizeof(m->print_date),
84
197980
	    daynames ? "%a %b %d" : "%b %d", tm) == 0)
85
		m->print_date[sizeof(m->print_date) - 1] = '\0';
86
98990
}
87
88
void
89
setnnames(void)
90
{
91
7310
	char buf[80];
92
	int i, l;
93
3655
	struct tm tm;
94
95
58480
	for (i = 0; i < 7; i++) {
96
25585
		tm.tm_wday = i;
97
25585
		l = strftime(buf, sizeof(buf), "%a", &tm);
98

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

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

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

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

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

5418000
				if (tp->tm_yday > 300 && tmtmp.tm_mon <= 1)
433
135450
					variable_weekday(&vwd, tmtmp.tm_mon + 1,
434
135450
					    tmtmp.tm_year + 1900 + 1);
435
				else
436
8939700
					variable_weekday(&vwd, tmtmp.tm_mon + 1,
437
4469850
					    tmtmp.tm_year + 1900);
438
4605300
				day = cumdays[tmtmp.tm_mon + 1] + vwd;
439
4605300
				tmtmp.tm_mday = vwd;
440
4605300
			}
441
7993485
			v2 = day - tp->tm_yday;
442
7993485
			if ((v2 > v1) || (v2 < 0)) {
443

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

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

49835
			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
49835
				tmp->when = f_time + v2 * SECSPERDAY;
458
				tmp->bodun = 0;
459
			}
460
461
49835
			(void)mktime(&tmtmp);
462
49835
			fill_print_date(tmp, &tmtmp);
463
49835
			tmp->var   = varp;
464
49835
			tmp->next  = NULL;
465
49835
			return(tmp);
466
		}
467
	} else {
468
		varp = 1;
469
		/* Set up v1 to the event number and ... */
470
10965
		v1 = vwd % (NUMEV + 1) - 1;
471
10965
		vwd /= (NUMEV + 1);
472
10965
		if (v1 < 0) {
473
3655
			v1 += NUMEV + 1;
474
3655
			vwd--;
475
3655
		}
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
4307520
	for (i = -1; i < 2; i++) {
489
1539720
		memcpy(&tmtmp, tp, sizeof(struct tm));
490
1539720
		tmtmp.tm_mday = dayp;
491
1539720
		tmtmp.tm_mon = month = monthp - 1;
492
1539720
		do {
493
			v2 = 0;
494
1540495
			switch (interval) {
495
			case WEEKLY:
496
59530
				tmtmp.tm_mday += 7 * i;
497
59530
				break;
498
			case MONTHLY:
499
1525295
				month += i;
500
1525295
				tmtmp.tm_mon = month;
501
1525295
				switch(tmtmp.tm_mon) {
502
				case -1:
503
48980
					tmtmp.tm_mon = month = 11;
504
					tmtmp.tm_year--;
505
48980
					break;
506
				case 12:
507
25275
					tmtmp.tm_mon = month = 0;
508
					tmtmp.tm_year++;
509
25275
					break;
510
				}
511
1525295
				if (vwd) {
512
1151020
					v1 = vwd;
513
2302040
					variable_weekday(&v1, tmtmp.tm_mon + 1,
514
1151020
					    tmtmp.tm_year + 1900);
515
1151020
					tmtmp.tm_mday = v1;
516
1151020
				} else
517
					tmtmp.tm_mday = dayp;
518
1451040
				break;
519
			case YEARLY:
520
			default:
521
29925
				tmtmp.tm_year += i;
522
29925
				if (flags & F_SPECIAL) {
523
29925
					tmtmp.tm_mon = 0;	/* Gee, mktime() is nice */
524
59850
					tmtmp.tm_mday = spev[v1].getev(tmtmp.tm_year +
525
29925
					    1900) + vwd;
526
29925
				} 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
1540495
			if ((ttmp = mktime(&tmtmp)) == -1)
541
				warnx("time out of range: %s", endp);
542
			else {
543
1540495
				tdiff = difftime(ttmp, f_time)/ SECSPERDAY;
544
1540495
				if (tdiff <= offset + f_dayAfter ||
545
614040
				    (bodun && tdiff == -1)) {
546
926455
					if (((tmtmp.tm_mon == month) ||
547
114570
					     (flags & F_SPECIAL) ||
548
57285
					     (interval == WEEKLY)) &&
549
891690
					    (tdiff >=  0 ||
550
842535
					    (bodun && tdiff == -1))) {
551
49155
					if ((tmp = malloc(sizeof(struct match))) == NULL)
552
						err(1, NULL);
553
49155
					tmp->when = ttmp;
554
49155
					fill_print_date(tmp, &tmtmp);
555
49155
					tmp->bodun = bodun && tdiff == -1;
556
49155
					tmp->var   = varp;
557
49155
					tmp->next  = NULL;
558
49155
					if (tmp2)
559
						tmp2->next = tmp;
560
					else
561
						matches = tmp;
562
					tmp2 = tmp;
563
49155
					v2 = (i == 1) ? 1 : 0;
564
49155
					}
565
				} else
566
					i = 2; /* No point checking in the future */
567
			}
568
1540495
		} while (v2 != 0);
569
	}
570
614040
	return (matches);
571
8607525
}
572
573
574
int
575
getmonth(char *s)
576
{
577
	char **p;
578
	struct fixs *n;
579
580
235228490
	for (n = fnmonths; n->name; ++n)
581
104847330
		if (!strncasecmp(s, n->name, n->len))
582
1399865
			return ((n - fnmonths) + 1);
583
177720720
	for (n = nmonths; n->name; ++n)
584
83224350
		if (!strncasecmp(s, n->name, n->len))
585
2408645
			return ((n - nmonths) + 1);
586
146536260
	for (p = months; *p; ++p)
587
67632120
		if (!strncasecmp(s, *p, 3))
588
			return ((p - months) + 1);
589
5636010
	return (0);
590
9444520
}
591
592
593
int
594
getday(char *s)
595
{
596
	char **p;
597
	struct fixs *n;
598
599
69203770
	for (n = fndays; n->name; ++n)
600
29473920
		if (!strncasecmp(s, n->name, n->len))
601
3326050
			return ((n - fndays) + 1);
602
22836440
	for (n = ndays; n->name; ++n)
603
10873625
		if (!strncasecmp(s, n->name, n->len))
604
1765365
			return ((n - ndays) + 1);
605
8713520
	for (p = days; *p; ++p)
606
3812165
		if (!strncasecmp(s, *p, 3))
607
			return ((p - days) + 1);
608
544595
	return (0);
609
5636010
}
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
10182830
	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
5091415
	switch(*(s + offset - 2)) {
629
	case '-':
630
	case '+':
631
5065830
	    return(atoi(s + offset - 2));
632
	    break;
633
	}
634
635
	/*
636
	 * some aliases: last, first, second, third, fourth
637
	 */
638
639
	/* last */
640

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

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

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

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

40205
	else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth"))
649
	    return(+4);
650
651
	/* no offset detected */
652
25585
	return(0);
653
5091415
}
654
655
656
int
657
foy(int year)
658
{
659
	/* 0-6; what weekday Jan 1 is */
660
11512640
	year--;
661
5756320
	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

14316700
	if (isleap(year))
674
2804060
		cumdays = daytab[1];
675
	else
676
		cumdays = daytab[0];
677
5756320
	day1 = foy(year);
678
	/* negative offset; last, -4 .. -1 */
679
5756320
	if (*day < 0) {
680
515440
		v1 = *day/10 - 1;          /* offset -4 ... -1 */
681
515440
		*day = 10 + (*day % 10);    /* day 1 ... 7 */
682
683
		/* which weekday the end of the month is (1-7) */
684
515440
		v2 = (cumdays[month + 1] + day1) % 7 + 1;
685
686
		/* and subtract enough days */
687
1546320
		*day = cumdays[month + 1] - cumdays[month] +
688
1030880
		    (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
515440
	}
693
694
	/* first, second ... +1 ... +5 */
695
	else {
696
		v1 = *day/10;        /* offset */
697
5240880
		*day = *day % 10;
698
699
		/* which weekday the first of the month is (1-7) */
700
5240880
		v2 = (cumdays[month] + 1 + day1) % 7 + 1;
701
702
		/* and add enough days */
703
5240880
		*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
5756320
}