parsedata.c revision 244166
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: head/usr.bin/calendar/parsedata.c 244166 2012-12-13 01:44:58Z grog $");
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
40static char *showflags(int flags);
41static int isonlydigits(char *s, int nostar);
42static const char *getmonthname(int i);
43static int checkmonth(char *s, size_t *len, size_t *offset, const char **month);
44static const char *getdayofweekname(int i);
45static int checkdayofweek(char *s, size_t *len, size_t *offset, const char **dow);
46static int indextooffset(char *s);
47static int parseoffset(char *s);
48static char *floattoday(int year, double f);
49static 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 */
86static int
87determinestyle(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 */
310fail:
311	*p = pold;
312	return (0);
313allfine:
314	*p = pold;
315	return (1);
316
317}
318
319static void
320remember(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
341static void
342debug_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
370struct 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 */
390int
391parsedaymonth(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
773static char *
774showflags(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
821static const char *
822getmonthname(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
831static int
832checkmonth(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
874static const char *
875getdayofweekname(int i)
876{
877	if (ndays[i].len != 0 && ndays[i].name != NULL)
878		return (ndays[i].name);
879	return (days[i]);
880}
881
882static int
883checkdayofweek(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
925static int
926isonlydigits(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
938static int
939indextooffset(char *s)
940{
941	int i;
942	struct fixs *n;
943	char *es;
944
945	if (s[0] == '+' || s[0] == '-') {
946		i = strtod (s, &es);
947		if (*es != '\0')                      /* trailing junk */
948			errx (1, "Invalid specifier format: %s\n", s);
949		return (i);
950	}
951
952	for (i = 0; i < 6; i++) {
953		if (strcasecmp(s, sequences[i]) == 0) {
954			if (i == 5)
955				return (-1);
956			return (i + 1);
957		}
958	}
959	for (i = 0; i < 6; i++) {
960		n = nsequences + i;
961		if (n->len == 0)
962			continue;
963		if (strncasecmp(s, n->name, n->len) == 0) {
964			if (i == 5)
965				return (-1);
966			return (i + 1);
967		}
968	}
969	return (0);
970}
971
972static int
973parseoffset(char *s)
974{
975
976	return strtol(s, NULL, 10);
977}
978
979static char *
980floattotime(double f)
981{
982	static char buf[100];
983	int hh, mm, ss, i;
984
985	f -= floor(f);
986	i = f * SECSPERDAY;
987
988	hh = i / SECSPERHOUR;
989	i %= SECSPERHOUR;
990	mm = i / SECSPERMINUTE;
991	i %= SECSPERMINUTE;
992	ss = i;
993
994	sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
995	return (buf);
996}
997
998static char *
999floattoday(int year, double f)
1000{
1001	static char buf[100];
1002	int i, m, d, hh, mm, ss;
1003	int *cumdays = cumdaytab[isleap(year)];
1004
1005	for (i = 0; 1 + cumdays[i] < f; i++)
1006		;
1007	m = --i;
1008	d = floor(f - 1 - cumdays[i]);
1009	f -= floor(f);
1010	i = f * SECSPERDAY;
1011
1012	hh = i / SECSPERHOUR;
1013	i %= SECSPERHOUR;
1014	mm = i / SECSPERMINUTE;
1015	i %= SECSPERMINUTE;
1016	ss = i;
1017
1018	sprintf(buf, "%02d-%02d %02d:%02d:%02d", m, d, hh, mm, ss);
1019	return (buf);
1020}
1021
1022void
1023dodebug(char *what)
1024{
1025	int year;
1026
1027	printf("UTCOffset: %g\n", UTCOffset);
1028	printf("eastlongitude: %d\n", EastLongitude);
1029
1030	if (strcmp(what, "moon") == 0) {
1031		double ffullmoon[MAXMOONS], fnewmoon[MAXMOONS];
1032		int i;
1033
1034		for (year = year1; year <= year2; year++) {
1035			fpom(year, UTCOffset, ffullmoon, fnewmoon);
1036			printf("Full moon %d:\t", year);
1037			for (i = 0; ffullmoon[i] >= 0; i++) {
1038				printf("%g (%s) ", ffullmoon[i],
1039				    floattoday(year, ffullmoon[i]));
1040			}
1041			printf("\nNew moon %d:\t", year);
1042			for (i = 0; fnewmoon[i] >= 0; i++) {
1043				printf("%g (%s) ", fnewmoon[i],
1044				    floattoday(year, fnewmoon[i]));
1045			}
1046			printf("\n");
1047
1048		}
1049
1050		return;
1051	}
1052
1053	if (strcmp(what, "sun") == 0) {
1054		double equinoxdays[2], solsticedays[2];
1055		for (year = year1; year <= year2; year++) {
1056			printf("Sun in %d:\n", year);
1057			fequinoxsolstice(year, UTCOffset, equinoxdays,
1058			    solsticedays);
1059			printf("e[0] - %g (%s)\n",
1060			    equinoxdays[0],
1061			    floattoday(year, equinoxdays[0]));
1062			printf("e[1] - %g (%s)\n",
1063			    equinoxdays[1],
1064			    floattoday(year, equinoxdays[1]));
1065			printf("s[0] - %g (%s)\n",
1066			    solsticedays[0],
1067			    floattoday(year, solsticedays[0]));
1068			printf("s[1] - %g (%s)\n",
1069			    solsticedays[1],
1070			    floattoday(year, solsticedays[1]));
1071		}
1072		return;
1073	}
1074}
1075