xref: /freebsd/usr.bin/calendar/parsedata.c (revision aa77200569e397d6ff1fdb4d255d0fa254d0a128)
1 /*-
2  * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <ctype.h>
32 #include <math.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <err.h>
37 
38 #include "calendar.h"
39 
40 static char *showflags(int flags);
41 static int isonlydigits(char *s, int nostar);
42 static const char *getmonthname(int i);
43 static int checkmonth(char *s, size_t *len, size_t *offset, const char **month);
44 static const char *getdayofweekname(int i);
45 static int checkdayofweek(char *s, size_t *len, size_t *offset, const char **dow);
46 static int indextooffset(char *s);
47 static int parseoffset(char *s);
48 static char *floattoday(int year, double f);
49 static char *floattotime(double f);
50 
51 /*
52  * Expected styles:
53  *
54  * Date			::=	Month . ' ' . DayOfMonth |
55  *				Month . ' ' . DayOfWeek . ModifierIndex |
56  *				Month . '/' . DayOfMonth |
57  *				Month . '/' . DayOfWeek . ModifierIndex |
58  *				DayOfMonth . ' ' . Month |
59  *				DayOfMonth . '/' . Month |
60  *				DayOfWeek . ModifierIndex . ' ' .Month |
61  *				DayOfWeek . ModifierIndex . '/' .Month |
62  *				DayOfWeek . ModifierIndex |
63  *				SpecialDay . ModifierOffset
64  *
65  * Month		::=	MonthName | MonthNumber | '*'
66  * MonthNumber		::=	'0' ... '9' | '00' ... '09' | '10' ... '12'
67  * MonthName		::=	MonthNameShort | MonthNameLong
68  * MonthNameLong	::=	'January' ... 'December'
69  * MonthNameShort	::=	'Jan' ... 'Dec' | 'Jan.' ... 'Dec.'
70  *
71  * DayOfWeek		::=	DayOfWeekShort | DayOfWeekLong
72  * DayOfWeekShort	::=	'Mon' .. 'Sun'
73  * DayOfWeekLong	::=	'Monday' .. 'Sunday'
74  * DayOfMonth		::=	'0' ... '9' | '00' ... '09' | '10' ... '29' |
75  *				'30' ... '31' | '*'
76  *
77  * ModifierOffset	::=	'' | '+' . ModifierNumber | '-' . ModifierNumber
78  * ModifierNumber	::=	'0' ... '9' | '00' ... '99' | '000' ... '299' |
79  *				'300' ... '359' | '360' ... '365'
80  * ModifierIndex	::=	'Second' | 'Third' | 'Fourth' | 'Fifth' |
81  *				'First' | 'Last'
82  *
83  * SpecialDay		::=	'Easter' | 'Paskha' | 'ChineseNewYear'
84  *
85  */
86 static int
87 determinestyle(char *date, int *flags,
88     char *month, int *imonth, char *dayofmonth, int *idayofmonth,
89     char *dayofweek, int *idayofweek, char *modifieroffset,
90     char *modifierindex, char *specialday, char *year, int *iyear)
91 {
92 	char *p, *p1, *p2, *py;
93 	const char *dow, *pmonth;
94 	char pold;
95 	size_t len, offset;
96 
97 	*flags = F_NONE;
98 	*month = '\0';
99 	*imonth = 0;
100 	*year = '\0';
101 	*iyear = 0;
102 	*dayofmonth = '\0';
103 	*idayofmonth = 0;
104 	*dayofweek = '\0';
105 	*idayofweek = 0;
106 	*modifieroffset = '\0';
107 	*modifierindex = '\0';
108 	*specialday = '\0';
109 
110 #define CHECKSPECIAL(s1, s2, lens2, type)				\
111 	if (s2 != NULL && strncmp(s1, s2, lens2) == 0) {		\
112 		*flags |= F_SPECIALDAY;					\
113 		*flags |= type;						\
114 		*flags |= F_VARIABLE;					\
115 		if (strlen(s1) == lens2) {				\
116 			strcpy(specialday, s1);				\
117 			return (1);					\
118 		}							\
119 		strncpy(specialday, s1, lens2);				\
120 		specialday[lens2] = '\0';				\
121 		strcpy(modifieroffset, s1 + lens2);			\
122 		*flags |= F_MODIFIEROFFSET;				\
123 		return (1);						\
124 	}
125 
126 	if ((p = strchr(date, ' ')) == NULL) {
127 		if ((p = strchr(date, '/')) == NULL) {
128 			CHECKSPECIAL(date, STRING_CNY, strlen(STRING_CNY),
129 			    F_CNY);
130 			CHECKSPECIAL(date, ncny.name, ncny.len, F_CNY);
131 			CHECKSPECIAL(date, STRING_NEWMOON,
132 			    strlen(STRING_NEWMOON), F_NEWMOON);
133 			CHECKSPECIAL(date, nnewmoon.name, nnewmoon.len,
134 			    F_NEWMOON);
135 			CHECKSPECIAL(date, STRING_FULLMOON,
136 			    strlen(STRING_FULLMOON), F_FULLMOON);
137 			CHECKSPECIAL(date, nfullmoon.name, nfullmoon.len,
138 			    F_FULLMOON);
139 			CHECKSPECIAL(date, STRING_PASKHA,
140 			    strlen(STRING_PASKHA), F_PASKHA);
141 			CHECKSPECIAL(date, npaskha.name, npaskha.len, F_PASKHA);
142 			CHECKSPECIAL(date, STRING_EASTER,
143 			    strlen(STRING_EASTER), F_EASTER);
144 			CHECKSPECIAL(date, neaster.name, neaster.len, F_EASTER);
145 			CHECKSPECIAL(date, STRING_MAREQUINOX,
146 			    strlen(STRING_MAREQUINOX), F_MAREQUINOX);
147 			CHECKSPECIAL(date, nmarequinox.name, nmarequinox.len,
148 			    F_SEPEQUINOX);
149 			CHECKSPECIAL(date, STRING_SEPEQUINOX,
150 			    strlen(STRING_SEPEQUINOX), F_SEPEQUINOX);
151 			CHECKSPECIAL(date, nsepequinox.name, nsepequinox.len,
152 			    F_SEPEQUINOX);
153 			CHECKSPECIAL(date, STRING_JUNSOLSTICE,
154 			    strlen(STRING_JUNSOLSTICE), F_JUNSOLSTICE);
155 			CHECKSPECIAL(date, njunsolstice.name, njunsolstice.len,
156 			    F_JUNSOLSTICE);
157 			CHECKSPECIAL(date, STRING_DECSOLSTICE,
158 			    strlen(STRING_DECSOLSTICE), F_DECSOLSTICE);
159 			CHECKSPECIAL(date, ndecsolstice.name, ndecsolstice.len,
160 			    F_DECSOLSTICE);
161 			if (checkdayofweek(date, &len, &offset, &dow) != 0) {
162 				*flags |= F_DAYOFWEEK;
163 				*flags |= F_VARIABLE;
164 				*idayofweek = offset;
165 				if (strlen(date) == len) {
166 					strcpy(dayofweek, date);
167 					return (1);
168 				}
169 				strncpy(dayofweek, date, len);
170 				dayofweek[len] = '\0';
171 				strcpy(modifierindex, date + len);
172 				*flags |= F_MODIFIERINDEX;
173 				return (1);
174 			}
175 			if (isonlydigits(date, 1)) {
176 				/* Assume month number only */
177 				*flags |= F_MONTH;
178 				*imonth = (int)strtol(date, (char **)NULL, 10);
179 				strcpy(month, getmonthname(*imonth));
180 				return(1);
181 			}
182 			return (0);
183 		}
184 	}
185 
186 	/*
187 	 * AFTER this, leave by goto-ing to "allfine" or "fail" to restore the
188 	 * original data in `date'.
189 	 */
190 	pold = *p;
191 	*p = 0;
192 	p1 = date;
193 	p2 = p + 1;
194 	/* Now p2 points to the next field and p1 to the first field */
195 
196 	if ((py = strchr(p2, '/')) != NULL) {
197 		/* We have a year in the string. Now this is getting tricky */
198 		strcpy(year, p1);
199 		*iyear = (int)strtol(year, NULL, 10);
200 		p1 = p2;
201 		p2 = py + 1;
202 		*py = 0;
203 		*flags |= F_YEAR;
204 	}
205 
206 	/*
207 	printf("p1: %s\n", p1);
208 	printf("p2: %s\n", p2);
209 	printf("year: %s\n", year);
210 	*/
211 
212 	/* Check if there is a month-string in the date */
213 	if ((checkmonth(p1, &len, &offset, &pmonth) != 0)
214 	 || (checkmonth(p2, &len, &offset, &pmonth) != 0 && (p2 = p1))) {
215 		/* p2 is the non-month part */
216 		*flags |= F_MONTH;
217 		*imonth = offset;
218 
219 		strcpy(month, getmonthname(offset));
220 		if (isonlydigits(p2, 1)) {
221 			strcpy(dayofmonth, p2);
222 			*idayofmonth = (int)strtol(p2, (char **)NULL, 10);
223 			*flags |= F_DAYOFMONTH;
224 			goto allfine;
225 		}
226 		if (strcmp(p2, "*") == 0) {
227 			*flags |= F_ALLDAY;
228 			goto allfine;
229 		}
230 
231 		if (checkdayofweek(p2, &len, &offset, &dow) != 0) {
232 			*flags |= F_DAYOFWEEK;
233 			*flags |= F_VARIABLE;
234 			*idayofweek = offset;
235 			strcpy(dayofweek, getdayofweekname(offset));
236 			if (strlen(p2) == len)
237 				goto allfine;
238 			strcpy(modifierindex, p2 + len);
239 			*flags |= F_MODIFIERINDEX;
240 			goto allfine;
241 		}
242 
243 		goto fail;
244 	}
245 
246 	/* Check if there is an every-day or every-month in the string */
247 	if ((strcmp(p1, "*") == 0 && isonlydigits(p2, 1))
248 	 || (strcmp(p2, "*") == 0 && isonlydigits(p1, 1) && (p2 = p1))) {
249 		int d;
250 
251 		*flags |= F_ALLMONTH;
252 		*flags |= F_DAYOFMONTH;
253 		d = (int)strtol(p2, (char **)NULL, 10);
254 		*idayofmonth = d;
255 		sprintf(dayofmonth, "%d", d);
256 		goto allfine;
257 	}
258 
259 	/* Month as a number, then a weekday */
260 	if (isonlydigits(p1, 1)
261 	 && checkdayofweek(p2, &len, &offset, &dow) != 0) {
262 		int d;
263 
264 		*flags |= F_MONTH;
265 		*flags |= F_DAYOFWEEK;
266 		*flags |= F_VARIABLE;
267 
268 		*idayofweek = offset;
269 		d = (int)strtol(p1, (char **)NULL, 10);
270 		*imonth = d;
271 		strcpy(month, getmonthname(d));
272 
273 		strcpy(dayofweek, getdayofweekname(offset));
274 		if (strlen(p2) == len)
275 			goto allfine;
276 		strcpy(modifierindex, p2 + len);
277 		*flags |= F_MODIFIERINDEX;
278 		goto allfine;
279 	}
280 
281 	/* If both the month and date are specified as numbers */
282 	if (isonlydigits(p1, 1) && isonlydigits(p2, 0)) {
283 		/* Now who wants to be this ambigious? :-( */
284 		int m, d;
285 
286 		if (strchr(p2, '*') != NULL)
287 			*flags |= F_VARIABLE;
288 
289 		m = (int)strtol(p1, (char **)NULL, 10);
290 		d = (int)strtol(p2, (char **)NULL, 10);
291 
292 		*flags |= F_MONTH;
293 		*flags |= F_DAYOFMONTH;
294 
295 		if (m > 12) {
296 			*imonth = d;
297 			*idayofmonth = m;
298 			strcpy(month, getmonthname(d));
299 			sprintf(dayofmonth, "%d", m);
300 		} else {
301 			*imonth = m;
302 			*idayofmonth = d;
303 			strcpy(month, getmonthname(m));
304 			sprintf(dayofmonth, "%d", d);
305 		}
306 		goto allfine;
307 	}
308 
309 	/* FALLTHROUGH */
310 fail:
311 	*p = pold;
312 	return (0);
313 allfine:
314 	*p = pold;
315 	return (1);
316 
317 }
318 
319 static void
320 remember(int *rememberindex, int *y, int *m, int *d, char **ed, int yy, int mm,
321     int dd, char *extra)
322 {
323 	static int warned = 0;
324 
325 	if (*rememberindex >= MAXCOUNT - 1) {
326 		if (warned == 0)
327 			warnx("Index > %d, ignored", MAXCOUNT);
328 		warned++;
329 		return;
330 	}
331 	y[*rememberindex] = yy;
332 	m[*rememberindex] = mm;
333 	d[*rememberindex] = dd;
334 	if (extra != NULL)
335 		strcpy(ed[*rememberindex], extra);
336 	else
337 		ed[*rememberindex][0] = '\0';
338 	*rememberindex += 1;
339 }
340 
341 static void
342 debug_determinestyle(int dateonly, char *date, int flags, char *month,
343     int imonth, char *dayofmonth, int idayofmonth, char *dayofweek,
344     int idayofweek, char *modifieroffset, char *modifierindex, char *specialday,
345     char *year, int iyear)
346 {
347 
348 	if (dateonly != 0) {
349 		printf("-------\ndate: |%s|\n", date);
350 		if (dateonly == 1)
351 			return;
352 	}
353 	printf("flags: %x - %s\n", flags, showflags(flags));
354 	if (modifieroffset[0] != '\0')
355 		printf("modifieroffset: |%s|\n", modifieroffset);
356 	if (modifierindex[0] != '\0')
357 		printf("modifierindex: |%s|\n", modifierindex);
358 	if (year[0] != '\0')
359 		printf("year: |%s| (%d)\n", year, iyear);
360 	if (month[0] != '\0')
361 		printf("month: |%s| (%d)\n", month, imonth);
362 	if (dayofmonth[0] != '\0')
363 		printf("dayofmonth: |%s| (%d)\n", dayofmonth, idayofmonth);
364 	if (dayofweek[0] != '\0')
365 		printf("dayofweek: |%s| (%d)\n", dayofweek, idayofweek);
366 	if (specialday[0] != '\0')
367 		printf("specialday: |%s|\n", specialday);
368 }
369 
370 struct yearinfo {
371 	int year;
372 	int ieaster, ipaskha, firstcnyday;
373 	double ffullmoon[MAXMOONS], fnewmoon[MAXMOONS];
374 	double ffullmooncny[MAXMOONS], fnewmooncny[MAXMOONS];
375 	int ichinesemonths[MAXMOONS];
376 	double equinoxdays[2], solsticedays[2];
377 	int *mondays;
378 	struct yearinfo *next;
379 };
380 /*
381  * Possible date formats include any combination of:
382  *	3-charmonth			(January, Jan, Jan)
383  *	3-charweekday			(Friday, Monday, mon.)
384  *	numeric month or day		(1, 2, 04)
385  *
386  * Any character may separate them, or they may not be separated.  Any line,
387  * following a line that is matched, that starts with "whitespace", is shown
388  * along with the matched line.
389  */
390 int
391 parsedaymonth(char *date, int *yearp, int *monthp, int *dayp, int *flags,
392     char **edp)
393 {
394 	char month[100], dayofmonth[100], dayofweek[100], modifieroffset[100];
395 	char syear[100];
396 	char modifierindex[100], specialday[100];
397 	int idayofweek = -1, imonth = -1, idayofmonth = -1, iyear = -1;
398 	int year, remindex;
399 	int d, m, dow, rm, rd, offset;
400 	char *ed;
401 	int retvalsign = 1;
402 
403 	static struct yearinfo *years, *yearinfo;
404 
405 	/*
406 	 * CONVENTION
407 	 *
408 	 * Month:     1-12
409 	 * Monthname: Jan .. Dec
410 	 * Day:	      1-31
411 	 * Weekday:   Mon .. Sun
412 	 *
413 	 */
414 
415 	*flags = 0;
416 
417 	if (debug)
418 		debug_determinestyle(1, date, *flags, month, imonth,
419 		    dayofmonth, idayofmonth, dayofweek, idayofweek,
420 		    modifieroffset, modifierindex, specialday, syear, iyear);
421 	if (determinestyle(date, flags, month, &imonth, dayofmonth,
422 	    &idayofmonth, dayofweek, &idayofweek, modifieroffset,
423 	    modifierindex, specialday, syear, &iyear) == 0) {
424 		if (debug)
425 			printf("Failed!\n");
426 		return (0);
427 	}
428 
429 	if (debug)
430 		debug_determinestyle(0, date, *flags, month, imonth,
431 		    dayofmonth, idayofmonth, dayofweek, idayofweek,
432 		    modifieroffset, modifierindex, specialday, syear, iyear);
433 
434 	remindex = 0;
435 	for (year = year1; year <= year2; year++) {
436 
437 		int lflags = *flags;
438 		/* If the year is specified, only do it if it is this year! */
439 		if ((lflags & F_YEAR) != 0)
440 			if (iyear != year)
441 				continue;
442 		lflags &= ~F_YEAR;
443 
444 		/* Get important dates for this year */
445 		yearinfo = years;
446 		while (yearinfo != NULL) {
447 			if (yearinfo->year == year)
448 				break;
449 			yearinfo = yearinfo -> next;
450 		}
451 		if (yearinfo == NULL) {
452 			yearinfo = (struct yearinfo *)calloc(1,
453 			    sizeof(struct yearinfo));
454 			if (yearinfo == NULL)
455 				errx(1, "Unable to allocate more years");
456 			yearinfo->year = year;
457 			yearinfo->next = years;
458 			years = yearinfo;
459 
460 			yearinfo->mondays = mondaytab[isleap(year)];
461 			yearinfo->ieaster = easter(year);
462 			yearinfo->ipaskha = paskha(year);
463 			fpom(year, UTCOffset, yearinfo->ffullmoon,
464 			    yearinfo->fnewmoon);
465 			fpom(year, UTCOFFSET_CNY, yearinfo->ffullmooncny,
466 			    yearinfo->fnewmooncny);
467 			fequinoxsolstice(year, UTCOffset,
468 			    yearinfo->equinoxdays, yearinfo->solsticedays);
469 
470 			/*
471 			 * CNY: Match day with sun longitude at 330` with new
472 			 * moon
473 			 */
474 			yearinfo->firstcnyday = calculatesunlongitude30(year,
475 			    UTCOFFSET_CNY, yearinfo->ichinesemonths);
476 			for (m = 0; yearinfo->fnewmooncny[m] >= 0; m++) {
477 				if (yearinfo->fnewmooncny[m] >
478 				    yearinfo->firstcnyday) {
479 					yearinfo->firstcnyday =
480 					    floor(yearinfo->fnewmooncny[m - 1]);
481 					break;
482 				}
483 			}
484 		}
485 
486 		/* Same day every year */
487 		if (lflags == (F_MONTH | F_DAYOFMONTH)) {
488 			if (!remember_ymd(year, imonth, idayofmonth))
489 				continue;
490 			remember(&remindex, yearp, monthp, dayp, edp,
491 			    year, imonth, idayofmonth, NULL);
492 			continue;
493 		}
494 
495 		/* XXX Same day every year, but variable */
496 		if (lflags == (F_MONTH | F_DAYOFMONTH | F_VARIABLE)) {
497 			if (!remember_ymd(year, imonth, idayofmonth))
498 				continue;
499 			remember(&remindex, yearp, monthp, dayp, edp,
500 			    year, imonth, idayofmonth, NULL);
501 			continue;
502 		}
503 
504 		/* Same day every month */
505 		if (lflags == (F_ALLMONTH | F_DAYOFMONTH)) {
506 			for (m = 1; m <= 12; m++) {
507 				if (!remember_ymd(year, m, idayofmonth))
508 					continue;
509 				remember(&remindex, yearp, monthp, dayp, edp,
510 				    year, m, idayofmonth, NULL);
511 			}
512 			continue;
513 		}
514 
515 		/* Every day of a month */
516 		if (lflags == (F_ALLDAY | F_MONTH)) {
517 			for (d = 1; d <= yearinfo->mondays[imonth]; d++) {
518 				if (!remember_ymd(year, imonth, d))
519 					continue;
520 				remember(&remindex, yearp, monthp, dayp, edp,
521 				    year, imonth, d, NULL);
522 			}
523 			continue;
524 		}
525 
526 		/* One day of every month */
527 		if (lflags == (F_ALLMONTH | F_DAYOFWEEK)) {
528 			for (m = 1; m <= 12; m++) {
529 				if (!remember_ymd(year, m, idayofmonth))
530 					continue;
531 				remember(&remindex, yearp, monthp, dayp, edp,
532 				    year, m, idayofmonth, NULL);
533 			}
534 			continue;
535 		}
536 
537 		/* Every dayofweek of the year */
538 		if (lflags == (F_DAYOFWEEK | F_VARIABLE)) {
539 			dow = first_dayofweek_of_year(year);
540 			d = (idayofweek - dow + 8) % 7;
541 			while (d <= 366) {
542 				if (remember_yd(year, d, &rm, &rd))
543 					remember(&remindex,
544 					    yearp, monthp, dayp, edp,
545 					    year, rm, rd, NULL);
546 				d += 7;
547 			}
548 			continue;
549 		}
550 
551 		/* Every so-manied dayofweek of every month of the year */
552 		if (lflags == (F_DAYOFWEEK | F_MODIFIERINDEX | F_VARIABLE)) {
553 			offset = indextooffset(modifierindex);
554 
555 			for (m = 0; m < 12; m++) {
556 				dow = first_dayofweek_of_month(year, m);
557 				d = (idayofweek - dow + 8) % 7;
558 				d += (offset - 1) * 7;
559 				if (remember_ymd(year, m, d)) {
560 					remember(&remindex,
561 					    yearp, monthp, dayp, edp,
562 					    year, m, d, NULL);
563 					continue;
564 				}
565 			}
566 			continue;
567 		}
568 
569 		/* A certain dayofweek of a month */
570 		if (lflags ==
571 		    (F_MONTH | F_DAYOFWEEK | F_MODIFIERINDEX | F_VARIABLE)) {
572 			offset = indextooffset(modifierindex);
573 			dow = first_dayofweek_of_month(year, imonth);
574 			d = (idayofweek - dow + 8) % 7;
575 
576 			if (offset > 0) {
577 				while (d <= yearinfo->mondays[imonth]) {
578 					if (--offset == 0
579 					 && remember_ymd(year, imonth, d)) {
580 						remember(&remindex,
581 						    yearp, monthp, dayp, edp,
582 						    year, imonth, d, NULL);
583 						continue;
584 					}
585 					d += 7;
586 				}
587 				continue;
588 			}
589 			if (offset < 0) {
590 				while (d <= yearinfo->mondays[imonth])
591 					d += 7;
592 				while (offset != 0) {
593 					offset++;
594 					d -= 7;
595 				}
596 				if (remember_ymd(year, imonth, d))
597 					remember(&remindex,
598 					    yearp, monthp, dayp, edp,
599 					    year, imonth, d, NULL);
600 				continue;
601 			}
602 			continue;
603 		}
604 
605 		/* Every dayofweek of the month */
606 		if (lflags == (F_DAYOFWEEK | F_MONTH | F_VARIABLE)) {
607 			dow = first_dayofweek_of_month(year, imonth);
608 			d = (idayofweek - dow + 8) % 7;
609 			while (d <= yearinfo->mondays[imonth]) {
610 				if (remember_ymd(year, imonth, d))
611 					remember(&remindex,
612 					    yearp, monthp, dayp, edp,
613 					    year, imonth, d, NULL);
614 				d += 7;
615 			}
616 			continue;
617 		}
618 
619 		/* Easter */
620 		if ((lflags & ~F_MODIFIEROFFSET) ==
621 		    (F_SPECIALDAY | F_VARIABLE | F_EASTER)) {
622 			offset = 0;
623 			if ((lflags & F_MODIFIEROFFSET) != 0)
624 				offset = parseoffset(modifieroffset);
625 			if (remember_yd(year, yearinfo->ieaster + offset,
626 			    &rm, &rd))
627 				remember(&remindex, yearp, monthp, dayp, edp,
628 				    year, rm, rd, NULL);
629 			continue;
630 		}
631 
632 		/* Paskha */
633 		if ((lflags & ~F_MODIFIEROFFSET) ==
634 		    (F_SPECIALDAY | F_VARIABLE | F_PASKHA)) {
635 			offset = 0;
636 			if ((lflags & F_MODIFIEROFFSET) != 0)
637 				offset = parseoffset(modifieroffset);
638 			if (remember_yd(year, yearinfo->ipaskha + offset,
639 			    &rm, &rd))
640 				remember(&remindex, yearp, monthp, dayp, edp,
641 				    year, rm, rd, NULL);
642 			continue;
643 		}
644 
645 		/* Chinese New Year */
646 		if ((lflags & ~F_MODIFIEROFFSET) ==
647 		    (F_SPECIALDAY | F_VARIABLE | F_CNY)) {
648 			offset = 0;
649 			if ((lflags & F_MODIFIEROFFSET) != 0)
650 				offset = parseoffset(modifieroffset);
651 			if (remember_yd(year, yearinfo->firstcnyday + offset,
652 			    &rm, &rd))
653 				remember(&remindex, yearp, monthp, dayp, edp,
654 				    year, rm, rd, NULL);
655 			continue;
656 		}
657 
658 		/* FullMoon */
659 		if ((lflags & ~F_MODIFIEROFFSET) ==
660 		    (F_SPECIALDAY | F_VARIABLE | F_FULLMOON)) {
661 			int i;
662 
663 			offset = 0;
664 			if ((lflags & F_MODIFIEROFFSET) != 0)
665 				offset = parseoffset(modifieroffset);
666 			for (i = 0; yearinfo->ffullmoon[i] > 0; i++) {
667 				if (remember_yd(year,
668 				    floor(yearinfo->ffullmoon[i]) + offset,
669 					&rm, &rd)) {
670 					ed = floattotime(
671 					    yearinfo->ffullmoon[i]);
672 					remember(&remindex,
673 					    yearp, monthp, dayp, edp,
674 					    year, rm, rd, ed);
675 				}
676 			}
677 			continue;
678 		}
679 
680 		/* NewMoon */
681 		if ((lflags & ~F_MODIFIEROFFSET) ==
682 		    (F_SPECIALDAY | F_VARIABLE | F_NEWMOON)) {
683 			int i;
684 
685 			offset = 0;
686 			if ((lflags & F_MODIFIEROFFSET) != 0)
687 				offset = parseoffset(modifieroffset);
688 			for (i = 0; yearinfo->ffullmoon[i] > 0; i++) {
689 				if (remember_yd(year,
690 				    floor(yearinfo->fnewmoon[i]) + offset,
691 				    &rm, &rd)) {
692 					ed = floattotime(yearinfo->fnewmoon[i]);
693 					remember(&remindex,
694 					    yearp, monthp, dayp, edp,
695 					    year, rm, rd, ed);
696 				}
697 			}
698 			continue;
699 		}
700 
701 		/* (Mar|Sep)Equinox */
702 		if ((lflags & ~F_MODIFIEROFFSET) ==
703 		    (F_SPECIALDAY | F_VARIABLE | F_MAREQUINOX)) {
704 			offset = 0;
705 			if ((lflags & F_MODIFIEROFFSET) != 0)
706 				offset = parseoffset(modifieroffset);
707 			if (remember_yd(year, yearinfo->equinoxdays[0] + offset,
708 			    &rm, &rd)) {
709 				ed = floattotime(yearinfo->equinoxdays[0]);
710 				remember(&remindex, yearp, monthp, dayp, edp,
711 				    year, rm, rd, ed);
712 			}
713 			continue;
714 		}
715 		if ((lflags & ~F_MODIFIEROFFSET) ==
716 		    (F_SPECIALDAY | F_VARIABLE | F_SEPEQUINOX)) {
717 			offset = 0;
718 			if ((lflags & F_MODIFIEROFFSET) != 0)
719 				offset = parseoffset(modifieroffset);
720 			if (remember_yd(year, yearinfo->equinoxdays[1] + offset,
721 			    &rm, &rd)) {
722 				ed = floattotime(yearinfo->equinoxdays[1]);
723 				remember(&remindex, yearp, monthp, dayp, edp,
724 				    year, rm, rd, ed);
725 			}
726 			continue;
727 		}
728 
729 		/* (Jun|Dec)Solstice */
730 		if ((lflags & ~F_MODIFIEROFFSET) ==
731 		    (F_SPECIALDAY | F_VARIABLE | F_JUNSOLSTICE)) {
732 			offset = 0;
733 			if ((lflags & F_MODIFIEROFFSET) != 0)
734 				offset = parseoffset(modifieroffset);
735 			if (remember_yd(year,
736 			    yearinfo->solsticedays[0] + offset, &rm, &rd)) {
737 				ed = floattotime(yearinfo->solsticedays[0]);
738 				remember(&remindex, yearp, monthp, dayp, edp,
739 				    year, rm, rd, ed);
740 			}
741 			continue;
742 		}
743 		if ((lflags & ~F_MODIFIEROFFSET) ==
744 		    (F_SPECIALDAY | F_VARIABLE | F_DECSOLSTICE)) {
745 			offset = 0;
746 			if ((lflags & F_MODIFIEROFFSET) != 0)
747 				offset = parseoffset(modifieroffset);
748 			if (remember_yd(year,
749 			    yearinfo->solsticedays[1] + offset, &rm, &rd)) {
750 				ed = floattotime(yearinfo->solsticedays[1]);
751 				remember(&remindex, yearp, monthp, dayp, edp,
752 				    year, rm, rd, ed);
753 			}
754 			continue;
755 		}
756 
757 		if (debug) {
758 		    printf("Unprocessed:\n");
759 		    debug_determinestyle(2, date, lflags, month, imonth,
760 			dayofmonth, idayofmonth, dayofweek, idayofweek,
761 			modifieroffset, modifierindex, specialday, syear,
762 			iyear);
763 		}
764 		retvalsign = -1;
765 	}
766 
767 	if (retvalsign == -1)
768 		return (-remindex - 1);
769 	else
770 		return (remindex);
771 }
772 
773 static char *
774 showflags(int flags)
775 {
776 	static char s[1000];
777 	s[0] = '\0';
778 
779 	if ((flags & F_YEAR) != 0)
780 		strcat(s, "year ");
781 	if ((flags & F_MONTH) != 0)
782 		strcat(s, "month ");
783 	if ((flags & F_DAYOFWEEK) != 0)
784 		strcat(s, "dayofweek ");
785 	if ((flags & F_DAYOFMONTH) != 0)
786 		strcat(s, "dayofmonth ");
787 	if ((flags & F_MODIFIERINDEX) != 0)
788 		strcat(s, "modifierindex ");
789 	if ((flags & F_MODIFIEROFFSET) != 0)
790 		strcat(s, "modifieroffset ");
791 	if ((flags & F_SPECIALDAY) != 0)
792 		strcat(s, "specialday ");
793 	if ((flags & F_ALLMONTH) != 0)
794 		strcat(s, "allmonth ");
795 	if ((flags & F_ALLDAY) != 0)
796 		strcat(s, "allday ");
797 	if ((flags & F_VARIABLE) != 0)
798 		strcat(s, "variable ");
799 	if ((flags & F_CNY) != 0)
800 		strcat(s, "chinesenewyear ");
801 	if ((flags & F_PASKHA) != 0)
802 		strcat(s, "paskha ");
803 	if ((flags & F_EASTER) != 0)
804 		strcat(s, "easter ");
805 	if ((flags & F_FULLMOON) != 0)
806 		strcat(s, "fullmoon ");
807 	if ((flags & F_NEWMOON) != 0)
808 		strcat(s, "newmoon ");
809 	if ((flags & F_MAREQUINOX) != 0)
810 		strcat(s, "marequinox ");
811 	if ((flags & F_SEPEQUINOX) != 0)
812 		strcat(s, "sepequinox ");
813 	if ((flags & F_JUNSOLSTICE) != 0)
814 		strcat(s, "junsolstice ");
815 	if ((flags & F_DECSOLSTICE) != 0)
816 		strcat(s, "decsolstice ");
817 
818 	return s;
819 }
820 
821 static const char *
822 getmonthname(int i)
823 {
824 	if (i <= 0 || i > 12)
825 		return ("");
826 	if (nmonths[i - 1].len != 0 && nmonths[i - 1].name != NULL)
827 		return (nmonths[i - 1].name);
828 	return (months[i - 1]);
829 }
830 
831 static int
832 checkmonth(char *s, size_t *len, size_t *offset, const char **month)
833 {
834 	struct fixs *n;
835 	int i;
836 
837 	for (i = 0; fnmonths[i].name != NULL; i++) {
838 		n = fnmonths + i;
839 		if (strncasecmp(s, n->name, n->len) == 0) {
840 			*len = n->len;
841 			*month = n->name;
842 			*offset = i + 1;
843 			return (1);
844 		}
845 	}
846 	for (i = 0; nmonths[i].name != NULL; i++) {
847 		n = nmonths + i;
848 		if (strncasecmp(s, n->name, n->len) == 0) {
849 			*len = n->len;
850 			*month = n->name;
851 			*offset = i + 1;
852 			return (1);
853 		}
854 	}
855 	for (i = 0; fmonths[i] != NULL; i++) {
856 		*len = strlen(fmonths[i]);
857 		if (strncasecmp(s, fmonths[i], *len) == 0) {
858 			*month = fmonths[i];
859 			*offset = i + 1;
860 			return (1);
861 		}
862 	}
863 	for (i = 0; months[i] != NULL; i++) {
864 		if (strncasecmp(s, months[i], 3) == 0) {
865 			*len = 3;
866 			*month = months[i];
867 			*offset = i + 1;
868 			return (1);
869 		}
870 	}
871 	return (0);
872 }
873 
874 static const char *
875 getdayofweekname(int i)
876 {
877 	if (ndays[i].len != 0 && ndays[i].name != NULL)
878 		return (ndays[i].name);
879 	return (days[i]);
880 }
881 
882 static int
883 checkdayofweek(char *s, size_t *len, size_t *offset, const char **dow)
884 {
885 	struct fixs *n;
886 	int i;
887 
888 	for (i = 0; fndays[i].name != NULL; i++) {
889 		n = fndays + i;
890 		if (strncasecmp(s, n->name, n->len) == 0) {
891 			*len = n->len;
892 			*dow = n->name;
893 			*offset = i;
894 			return (1);
895 		}
896 	}
897 	for (i = 0; ndays[i].name != NULL; i++) {
898 		n = ndays + i;
899 		if (strncasecmp(s, n->name, n->len) == 0) {
900 			*len = n->len;
901 			*dow = n->name;
902 			*offset = i;
903 			return (1);
904 		}
905 	}
906 	for (i = 0; fdays[i] != NULL; i++) {
907 		*len = strlen(fdays[i]);
908 		if (strncasecmp(s, fdays[i], *len) == 0) {
909 			*dow = fdays[i];
910 			*offset = i;
911 			return (1);
912 		}
913 	}
914 	for (i = 0; days[i] != NULL; i++) {
915 		if (strncasecmp(s, days[i], 3) == 0) {
916 			*len = 3;
917 			*dow = days[i];
918 			*offset = i;
919 			return (1);
920 		}
921 	}
922 	return (0);
923 }
924 
925 static int
926 isonlydigits(char *s, int nostar)
927 {
928 	int i;
929 	for (i = 0; s[i] != '\0'; i++) {
930 		if (nostar == 0 && s[i] == '*' && s[i + 1] == '\0')
931 			return 1;
932 		if (!isdigit((unsigned char)s[i]))
933 			return (0);
934 	}
935 	return (1);
936 }
937 
938 static int
939 indextooffset(char *s)
940 {
941 	int i;
942 	struct fixs *n;
943 
944 	if (s[0] == '+' || s[0] == '-') {
945 		char ss[9];
946 		for (i = -100; i < 100; i++) {
947 			sprintf(ss, "%s%d", (i > 0) ? "+" : "", i);
948 			if (strcmp(ss, s) == 0)
949 				return (i);
950 		}
951 		return (0);
952 	}
953 
954 	for (i = 0; i < 6; i++) {
955 		if (strcasecmp(s, sequences[i]) == 0) {
956 			if (i == 5)
957 				return (-1);
958 			return (i + 1);
959 		}
960 	}
961 	for (i = 0; i < 6; i++) {
962 		n = nsequences + i;
963 		if (n->len == 0)
964 			continue;
965 		if (strncasecmp(s, n->name, n->len) == 0) {
966 			if (i == 5)
967 				return (-1);
968 			return (i + 1);
969 		}
970 	}
971 	return (0);
972 }
973 
974 static int
975 parseoffset(char *s)
976 {
977 
978 	return strtol(s, NULL, 10);
979 }
980 
981 static char *
982 floattotime(double f)
983 {
984 	static char buf[100];
985 	int hh, mm, ss, i;
986 
987 	f -= floor(f);
988 	i = f * SECSPERDAY;
989 
990 	hh = i / SECSPERHOUR;
991 	i %= SECSPERHOUR;
992 	mm = i / SECSPERMINUTE;
993 	i %= SECSPERMINUTE;
994 	ss = i;
995 
996 	sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
997 	return (buf);
998 }
999 
1000 static char *
1001 floattoday(int year, double f)
1002 {
1003 	static char buf[100];
1004 	int i, m, d, hh, mm, ss;
1005 	int *cumdays = cumdaytab[isleap(year)];
1006 
1007 	for (i = 0; 1 + cumdays[i] < f; i++)
1008 		;
1009 	m = --i;
1010 	d = floor(f - 1 - cumdays[i]);
1011 	f -= floor(f);
1012 	i = f * SECSPERDAY;
1013 
1014 	hh = i / SECSPERHOUR;
1015 	i %= SECSPERHOUR;
1016 	mm = i / SECSPERMINUTE;
1017 	i %= SECSPERMINUTE;
1018 	ss = i;
1019 
1020 	sprintf(buf, "%02d-%02d %02d:%02d:%02d", m, d, hh, mm, ss);
1021 	return (buf);
1022 }
1023 
1024 void
1025 dodebug(char *what)
1026 {
1027 	int year;
1028 
1029 	printf("UTCOffset: %g\n", UTCOffset);
1030 	printf("eastlongitude: %d\n", EastLongitude);
1031 
1032 	if (strcmp(what, "moon") == 0) {
1033 		double ffullmoon[MAXMOONS], fnewmoon[MAXMOONS];
1034 		int i;
1035 
1036 		for (year = year1; year <= year2; year++) {
1037 			fpom(year, UTCOffset, ffullmoon, fnewmoon);
1038 			printf("Full moon %d:\t", year);
1039 			for (i = 0; ffullmoon[i] >= 0; i++) {
1040 				printf("%g (%s) ", ffullmoon[i],
1041 				    floattoday(year, ffullmoon[i]));
1042 			}
1043 			printf("\nNew moon %d:\t", year);
1044 			for (i = 0; fnewmoon[i] >= 0; i++) {
1045 				printf("%g (%s) ", fnewmoon[i],
1046 				    floattoday(year, fnewmoon[i]));
1047 			}
1048 			printf("\n");
1049 
1050 		}
1051 
1052 		return;
1053 	}
1054 
1055 	if (strcmp(what, "sun") == 0) {
1056 		double equinoxdays[2], solsticedays[2];
1057 		for (year = year1; year <= year2; year++) {
1058 			printf("Sun in %d:\n", year);
1059 			fequinoxsolstice(year, UTCOffset, equinoxdays,
1060 			    solsticedays);
1061 			printf("e[0] - %g (%s)\n",
1062 			    equinoxdays[0],
1063 			    floattoday(year, equinoxdays[0]));
1064 			printf("e[1] - %g (%s)\n",
1065 			    equinoxdays[1],
1066 			    floattoday(year, equinoxdays[1]));
1067 			printf("s[0] - %g (%s)\n",
1068 			    solsticedays[0],
1069 			    floattoday(year, solsticedays[0]));
1070 			printf("s[1] - %g (%s)\n",
1071 			    solsticedays[1],
1072 			    floattoday(year, solsticedays[1]));
1073 		}
1074 		return;
1075 	}
1076 }
1077