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