xref: /freebsd/usr.bin/calendar/io.c (revision ac04cf18)
1fd1efedcSConrad Meyer /*-
2fd1efedcSConrad Meyer  * SPDX-License-Identifier: BSD-3-Clause
3fd1efedcSConrad Meyer  *
4fd1efedcSConrad Meyer  * Copyright (c) 1989, 1993, 1994
5fd1efedcSConrad Meyer  *	The Regents of the University of California.  All rights reserved.
6fd1efedcSConrad Meyer  *
7fd1efedcSConrad Meyer  * Redistribution and use in source and binary forms, with or without
8fd1efedcSConrad Meyer  * modification, are permitted provided that the following conditions
9fd1efedcSConrad Meyer  * are met:
10fd1efedcSConrad Meyer  * 1. Redistributions of source code must retain the above copyright
11fd1efedcSConrad Meyer  *    notice, this list of conditions and the following disclaimer.
12fd1efedcSConrad Meyer  * 2. Redistributions in binary form must reproduce the above copyright
13fd1efedcSConrad Meyer  *    notice, this list of conditions and the following disclaimer in the
14fd1efedcSConrad Meyer  *    documentation and/or other materials provided with the distribution.
15fd1efedcSConrad Meyer  * 3. Neither the name of the University nor the names of its contributors
16fd1efedcSConrad Meyer  *    may be used to endorse or promote products derived from this software
17fd1efedcSConrad Meyer  *    without specific prior written permission.
18fd1efedcSConrad Meyer  *
19fd1efedcSConrad Meyer  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20fd1efedcSConrad Meyer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21fd1efedcSConrad Meyer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22fd1efedcSConrad Meyer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23fd1efedcSConrad Meyer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24fd1efedcSConrad Meyer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25fd1efedcSConrad Meyer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26fd1efedcSConrad Meyer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27fd1efedcSConrad Meyer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28fd1efedcSConrad Meyer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29fd1efedcSConrad Meyer  * SUCH DAMAGE.
30fd1efedcSConrad Meyer  */
31fd1efedcSConrad Meyer 
32fd1efedcSConrad Meyer #ifndef lint
33fd1efedcSConrad Meyer static const char copyright[] =
34fd1efedcSConrad Meyer "@(#) Copyright (c) 1989, 1993\n\
35fd1efedcSConrad Meyer 	The Regents of the University of California.  All rights reserved.\n";
36fd1efedcSConrad Meyer #endif
37fd1efedcSConrad Meyer 
38fd1efedcSConrad Meyer #if 0
39fd1efedcSConrad Meyer #ifndef lint
40fd1efedcSConrad Meyer static char sccsid[] = "@(#)calendar.c  8.3 (Berkeley) 3/25/94";
41fd1efedcSConrad Meyer #endif
42fd1efedcSConrad Meyer #endif
43fd1efedcSConrad Meyer 
44fd1efedcSConrad Meyer #include <sys/cdefs.h>
45fd1efedcSConrad Meyer __FBSDID("$FreeBSD$");
46fd1efedcSConrad Meyer 
47fd1efedcSConrad Meyer #include <sys/param.h>
48fd1efedcSConrad Meyer #include <sys/stat.h>
49fd1efedcSConrad Meyer #include <sys/wait.h>
50fd1efedcSConrad Meyer #include <ctype.h>
51fd1efedcSConrad Meyer #include <err.h>
52fd1efedcSConrad Meyer #include <errno.h>
53fd1efedcSConrad Meyer #include <langinfo.h>
54fd1efedcSConrad Meyer #include <locale.h>
55fd1efedcSConrad Meyer #include <pwd.h>
56fd1efedcSConrad Meyer #include <stdbool.h>
57fd1efedcSConrad Meyer #include <stdio.h>
58fd1efedcSConrad Meyer #include <stdlib.h>
59fd1efedcSConrad Meyer #include <string.h>
60fd1efedcSConrad Meyer #include <stringlist.h>
61fd1efedcSConrad Meyer #include <time.h>
62fd1efedcSConrad Meyer #include <unistd.h>
63fd1efedcSConrad Meyer 
64fd1efedcSConrad Meyer #include "pathnames.h"
65fd1efedcSConrad Meyer #include "calendar.h"
66fd1efedcSConrad Meyer 
67fd1efedcSConrad Meyer enum {
68fd1efedcSConrad Meyer 	T_OK = 0,
69fd1efedcSConrad Meyer 	T_ERR,
70fd1efedcSConrad Meyer 	T_PROCESS,
71fd1efedcSConrad Meyer };
72fd1efedcSConrad Meyer 
73fd1efedcSConrad Meyer const char *calendarFile = "calendar";	/* default calendar file */
7434b38e12SStefan Eßer static const char *calendarHomes[] = {".calendar", _PATH_INCLUDE_LOCAL, _PATH_INCLUDE}; /* HOME */
75fd1efedcSConrad Meyer static const char *calendarNoMail = "nomail";/* don't sent mail if file exist */
76fd1efedcSConrad Meyer 
77fd1efedcSConrad Meyer static char path[MAXPATHLEN];
78fd1efedcSConrad Meyer 
79fd1efedcSConrad Meyer struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon;
80fd1efedcSConrad Meyer struct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice;
81fd1efedcSConrad Meyer 
82fd1efedcSConrad Meyer static int cal_parse(FILE *in, FILE *out);
83fd1efedcSConrad Meyer 
84fd1efedcSConrad Meyer static StringList *definitions = NULL;
85fd1efedcSConrad Meyer static struct event *events[MAXCOUNT];
86fd1efedcSConrad Meyer static char *extradata[MAXCOUNT];
87fd1efedcSConrad Meyer 
88fd1efedcSConrad Meyer static void
89fd1efedcSConrad Meyer trimlr(char **buf)
90fd1efedcSConrad Meyer {
91fd1efedcSConrad Meyer 	char *walk = *buf;
92fd1efedcSConrad Meyer 	char *last;
93fd1efedcSConrad Meyer 
94fd1efedcSConrad Meyer 	while (isspace(*walk))
95fd1efedcSConrad Meyer 		walk++;
96fd1efedcSConrad Meyer 	if (*walk != '\0') {
97fd1efedcSConrad Meyer 		last = walk + strlen(walk) - 1;
98fd1efedcSConrad Meyer 		while (last > walk && isspace(*last))
99fd1efedcSConrad Meyer 			last--;
100fd1efedcSConrad Meyer 		*(last+1) = 0;
101fd1efedcSConrad Meyer 	}
102fd1efedcSConrad Meyer 
103fd1efedcSConrad Meyer 	*buf = walk;
104fd1efedcSConrad Meyer }
105fd1efedcSConrad Meyer 
106fd1efedcSConrad Meyer static FILE *
107fd1efedcSConrad Meyer cal_fopen(const char *file)
108fd1efedcSConrad Meyer {
109fd1efedcSConrad Meyer 	FILE *fp;
110fd1efedcSConrad Meyer 	char *home = getenv("HOME");
111fd1efedcSConrad Meyer 	unsigned int i;
112d20d6550SWarner Losh 	struct stat sb;
113d20d6550SWarner Losh 	static bool warned = false;
114fd1efedcSConrad Meyer 
115fd1efedcSConrad Meyer 	if (home == NULL || *home == '\0') {
116fd1efedcSConrad Meyer 		warnx("Cannot get home directory");
117fd1efedcSConrad Meyer 		return (NULL);
118fd1efedcSConrad Meyer 	}
119fd1efedcSConrad Meyer 
120fd1efedcSConrad Meyer 	if (chdir(home) != 0) {
121fd1efedcSConrad Meyer 		warnx("Cannot enter home directory");
122fd1efedcSConrad Meyer 		return (NULL);
123fd1efedcSConrad Meyer 	}
124fd1efedcSConrad Meyer 
125fd1efedcSConrad Meyer 	for (i = 0; i < nitems(calendarHomes); i++) {
126fd1efedcSConrad Meyer 		if (chdir(calendarHomes[i]) != 0)
127fd1efedcSConrad Meyer 			continue;
128fd1efedcSConrad Meyer 
129fd1efedcSConrad Meyer 		if ((fp = fopen(file, "r")) != NULL)
130fd1efedcSConrad Meyer 			return (fp);
131fd1efedcSConrad Meyer 	}
132fd1efedcSConrad Meyer 
133fd1efedcSConrad Meyer 	warnx("can't open calendar file \"%s\"", file);
134d20d6550SWarner Losh 	if (!warned && stat(_PATH_INCLUDE_LOCAL, &sb) != 0) {
135d20d6550SWarner Losh 		warnx("calendar data files now provided by calendar-data pkg.");
136d20d6550SWarner Losh 		warned = true;
137d20d6550SWarner Losh 	}
138fd1efedcSConrad Meyer 
139fd1efedcSConrad Meyer 	return (NULL);
140fd1efedcSConrad Meyer }
141fd1efedcSConrad Meyer 
142fd1efedcSConrad Meyer static int
14319b5c307SStefan Eßer token(char *line, FILE *out, int *skip)
144fd1efedcSConrad Meyer {
145fd1efedcSConrad Meyer 	char *walk, c, a;
146fd1efedcSConrad Meyer 
147fd1efedcSConrad Meyer 	if (strncmp(line, "endif", 5) == 0) {
14819b5c307SStefan Eßer 		if (*skip > 0)
14919b5c307SStefan Eßer 			--*skip;
150fd1efedcSConrad Meyer 		return (T_OK);
151fd1efedcSConrad Meyer 	}
152fd1efedcSConrad Meyer 
15319b5c307SStefan Eßer 	if (strncmp(line, "ifdef", 5) == 0) {
15419b5c307SStefan Eßer 		walk = line + 5;
15519b5c307SStefan Eßer 		trimlr(&walk);
15619b5c307SStefan Eßer 
15719b5c307SStefan Eßer 		if (*walk == '\0') {
15819b5c307SStefan Eßer 			warnx("Expecting arguments after #ifdef");
15919b5c307SStefan Eßer 			return (T_ERR);
16019b5c307SStefan Eßer 		}
16119b5c307SStefan Eßer 
16219b5c307SStefan Eßer 		if (*skip != 0 || definitions == NULL || sl_find(definitions, walk) == NULL)
16319b5c307SStefan Eßer 			++*skip;
16419b5c307SStefan Eßer 
16519b5c307SStefan Eßer 		return (T_OK);
16619b5c307SStefan Eßer 	}
16719b5c307SStefan Eßer 
16819b5c307SStefan Eßer 	if (strncmp(line, "ifndef", 6) == 0) {
16919b5c307SStefan Eßer 		walk = line + 6;
17019b5c307SStefan Eßer 		trimlr(&walk);
17119b5c307SStefan Eßer 
17219b5c307SStefan Eßer 		if (*walk == '\0') {
17319b5c307SStefan Eßer 			warnx("Expecting arguments after #ifndef");
17419b5c307SStefan Eßer 			return (T_ERR);
17519b5c307SStefan Eßer 		}
17619b5c307SStefan Eßer 
17719b5c307SStefan Eßer 		if (*skip != 0 || (definitions != NULL && sl_find(definitions, walk) != NULL))
17819b5c307SStefan Eßer 			++*skip;
17919b5c307SStefan Eßer 
18019b5c307SStefan Eßer 		return (T_OK);
18119b5c307SStefan Eßer 	}
18219b5c307SStefan Eßer 
18319b5c307SStefan Eßer 	if (strncmp(line, "else", 4) == 0) {
18419b5c307SStefan Eßer 		walk = line + 4;
18519b5c307SStefan Eßer 		trimlr(&walk);
18619b5c307SStefan Eßer 
18719b5c307SStefan Eßer 		if (*walk != '\0') {
18819b5c307SStefan Eßer 			warnx("Expecting no arguments after #else");
18919b5c307SStefan Eßer 			return (T_ERR);
19019b5c307SStefan Eßer 		}
19119b5c307SStefan Eßer 
19219b5c307SStefan Eßer 		if (*skip == 0)
19319b5c307SStefan Eßer 			*skip = 1;
19419b5c307SStefan Eßer 		else if (*skip == 1)
19519b5c307SStefan Eßer 			*skip = 0;
19619b5c307SStefan Eßer 
19719b5c307SStefan Eßer 		return (T_OK);
19819b5c307SStefan Eßer 	}
19919b5c307SStefan Eßer 
20019b5c307SStefan Eßer 	if (*skip != 0)
201fd1efedcSConrad Meyer 		return (T_OK);
202fd1efedcSConrad Meyer 
203fd1efedcSConrad Meyer 	if (strncmp(line, "include", 7) == 0) {
204fd1efedcSConrad Meyer 		walk = line + 7;
205fd1efedcSConrad Meyer 
206fd1efedcSConrad Meyer 		trimlr(&walk);
207fd1efedcSConrad Meyer 
208fd1efedcSConrad Meyer 		if (*walk == '\0') {
209fd1efedcSConrad Meyer 			warnx("Expecting arguments after #include");
210fd1efedcSConrad Meyer 			return (T_ERR);
211fd1efedcSConrad Meyer 		}
212fd1efedcSConrad Meyer 
213fd1efedcSConrad Meyer 		if (*walk != '<' && *walk != '\"') {
214fd1efedcSConrad Meyer 			warnx("Excecting '<' or '\"' after #include");
215fd1efedcSConrad Meyer 			return (T_ERR);
216fd1efedcSConrad Meyer 		}
217fd1efedcSConrad Meyer 
218ac04cf18SStefan Eßer 		a = *walk == '<' ? '>' : '\"';
219fd1efedcSConrad Meyer 		walk++;
220fd1efedcSConrad Meyer 		c = walk[strlen(walk) - 1];
221fd1efedcSConrad Meyer 
222ac04cf18SStefan Eßer 		if (a != c) {
223ac04cf18SStefan Eßer 			warnx("Unterminated include expecting '%c'", a);
224fd1efedcSConrad Meyer 			return (T_ERR);
225fd1efedcSConrad Meyer 		}
226fd1efedcSConrad Meyer 		walk[strlen(walk) - 1] = '\0';
227fd1efedcSConrad Meyer 
228fd1efedcSConrad Meyer 		if (cal_parse(cal_fopen(walk), out))
229fd1efedcSConrad Meyer 			return (T_ERR);
230fd1efedcSConrad Meyer 
231fd1efedcSConrad Meyer 		return (T_OK);
232fd1efedcSConrad Meyer 	}
233fd1efedcSConrad Meyer 
234fd1efedcSConrad Meyer 	if (strncmp(line, "define", 6) == 0) {
235fd1efedcSConrad Meyer 		if (definitions == NULL)
236fd1efedcSConrad Meyer 			definitions = sl_init();
237fd1efedcSConrad Meyer 		walk = line + 6;
238fd1efedcSConrad Meyer 		trimlr(&walk);
239fd1efedcSConrad Meyer 
240fd1efedcSConrad Meyer 		if (*walk == '\0') {
241fd1efedcSConrad Meyer 			warnx("Expecting arguments after #define");
242fd1efedcSConrad Meyer 			return (T_ERR);
243fd1efedcSConrad Meyer 		}
244fd1efedcSConrad Meyer 
245fd1efedcSConrad Meyer 		sl_add(definitions, strdup(walk));
246fd1efedcSConrad Meyer 		return (T_OK);
247fd1efedcSConrad Meyer 	}
248fd1efedcSConrad Meyer 
249fd1efedcSConrad Meyer 	return (T_PROCESS);
250fd1efedcSConrad Meyer 
251fd1efedcSConrad Meyer }
252fd1efedcSConrad Meyer 
253fd1efedcSConrad Meyer #define	REPLACE(string, slen, struct_) \
254fd1efedcSConrad Meyer 		if (strncasecmp(buf, (string), (slen)) == 0 && buf[(slen)]) { \
255fd1efedcSConrad Meyer 			if (struct_.name != NULL)			      \
256fd1efedcSConrad Meyer 				free(struct_.name);			      \
257fd1efedcSConrad Meyer 			if ((struct_.name = strdup(buf + (slen))) == NULL)    \
258fd1efedcSConrad Meyer 				errx(1, "cannot allocate memory");	      \
259fd1efedcSConrad Meyer 			struct_.len = strlen(buf + (slen));		      \
260fd1efedcSConrad Meyer 			continue;					      \
261fd1efedcSConrad Meyer 		}
262fd1efedcSConrad Meyer static int
263fd1efedcSConrad Meyer cal_parse(FILE *in, FILE *out)
264fd1efedcSConrad Meyer {
265fd1efedcSConrad Meyer 	char *line = NULL;
266fd1efedcSConrad Meyer 	char *buf;
267fd1efedcSConrad Meyer 	size_t linecap = 0;
268fd1efedcSConrad Meyer 	ssize_t linelen;
269fd1efedcSConrad Meyer 	ssize_t l;
270fd1efedcSConrad Meyer 	static int d_first = -1;
271fd1efedcSConrad Meyer 	static int count = 0;
272fd1efedcSConrad Meyer 	int i;
273fd1efedcSConrad Meyer 	int month[MAXCOUNT];
274fd1efedcSConrad Meyer 	int day[MAXCOUNT];
275fd1efedcSConrad Meyer 	int year[MAXCOUNT];
27619b5c307SStefan Eßer 	int skip = 0;
277fd1efedcSConrad Meyer 	char dbuf[80];
278fd1efedcSConrad Meyer 	char *pp, p;
279fd1efedcSConrad Meyer 	struct tm tm;
280fd1efedcSConrad Meyer 	int flags;
281fd1efedcSConrad Meyer 
282fd1efedcSConrad Meyer 	/* Unused */
283fd1efedcSConrad Meyer 	tm.tm_sec = 0;
284fd1efedcSConrad Meyer 	tm.tm_min = 0;
285fd1efedcSConrad Meyer 	tm.tm_hour = 0;
286fd1efedcSConrad Meyer 	tm.tm_wday = 0;
287fd1efedcSConrad Meyer 
288fd1efedcSConrad Meyer 	if (in == NULL)
289fd1efedcSConrad Meyer 		return (1);
290fd1efedcSConrad Meyer 
291fd1efedcSConrad Meyer 	while ((linelen = getline(&line, &linecap, in)) > 0) {
292fd1efedcSConrad Meyer 		if (*line == '#') {
293fd1efedcSConrad Meyer 			switch (token(line+1, out, &skip)) {
294fd1efedcSConrad Meyer 			case T_ERR:
295fd1efedcSConrad Meyer 				free(line);
296fd1efedcSConrad Meyer 				return (1);
297fd1efedcSConrad Meyer 			case T_OK:
298fd1efedcSConrad Meyer 				continue;
299fd1efedcSConrad Meyer 			case T_PROCESS:
300fd1efedcSConrad Meyer 				break;
301fd1efedcSConrad Meyer 			default:
302fd1efedcSConrad Meyer 				break;
303fd1efedcSConrad Meyer 			}
304fd1efedcSConrad Meyer 		}
305fd1efedcSConrad Meyer 
30619b5c307SStefan Eßer 		if (skip != 0)
307fd1efedcSConrad Meyer 			continue;
308fd1efedcSConrad Meyer 
309fd1efedcSConrad Meyer 		buf = line;
310fd1efedcSConrad Meyer 		for (l = linelen;
311fd1efedcSConrad Meyer 		     l > 0 && isspace((unsigned char)buf[l - 1]);
312fd1efedcSConrad Meyer 		     l--)
313fd1efedcSConrad Meyer 			;
314fd1efedcSConrad Meyer 		buf[l] = '\0';
315fd1efedcSConrad Meyer 		if (buf[0] == '\0')
316fd1efedcSConrad Meyer 			continue;
317fd1efedcSConrad Meyer 
318fd1efedcSConrad Meyer 		/*
319fd1efedcSConrad Meyer 		 * Setting LANG in user's calendar was an old workaround
320fd1efedcSConrad Meyer 		 * for 'calendar -a' being run with C locale to properly
321fd1efedcSConrad Meyer 		 * print user's calendars in their native languages.
322fd1efedcSConrad Meyer 		 * Now that 'calendar -a' does fork with setusercontext(),
323fd1efedcSConrad Meyer 		 * and does not run iconv(), this variable has little use.
324fd1efedcSConrad Meyer 		 */
325fd1efedcSConrad Meyer 		if (strncmp(buf, "LANG=", 5) == 0) {
326fd1efedcSConrad Meyer 			(void)setlocale(LC_ALL, buf + 5);
327fd1efedcSConrad Meyer 			d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
328fd1efedcSConrad Meyer #ifdef WITH_ICONV
329fd1efedcSConrad Meyer 			if (!doall)
330fd1efedcSConrad Meyer 				set_new_encoding();
331fd1efedcSConrad Meyer #endif
332fd1efedcSConrad Meyer 			setnnames();
333fd1efedcSConrad Meyer 			continue;
334fd1efedcSConrad Meyer 		}
335fd1efedcSConrad Meyer 		/* Parse special definitions: Easter, Paskha etc */
336fd1efedcSConrad Meyer 		REPLACE("Easter=", 7, neaster);
337fd1efedcSConrad Meyer 		REPLACE("Paskha=", 7, npaskha);
338fd1efedcSConrad Meyer 		REPLACE("ChineseNewYear=", 15, ncny);
339fd1efedcSConrad Meyer 		REPLACE("NewMoon=", 8, nnewmoon);
340fd1efedcSConrad Meyer 		REPLACE("FullMoon=", 9, nfullmoon);
341fd1efedcSConrad Meyer 		REPLACE("MarEquinox=", 11, nmarequinox);
342fd1efedcSConrad Meyer 		REPLACE("SepEquinox=", 11, nsepequinox);
343fd1efedcSConrad Meyer 		REPLACE("JunSolstice=", 12, njunsolstice);
344fd1efedcSConrad Meyer 		REPLACE("DecSolstice=", 12, ndecsolstice);
345fd1efedcSConrad Meyer 		if (strncmp(buf, "SEQUENCE=", 9) == 0) {
346fd1efedcSConrad Meyer 			setnsequences(buf + 9);
347fd1efedcSConrad Meyer 			continue;
348fd1efedcSConrad Meyer 		}
349fd1efedcSConrad Meyer 
350fd1efedcSConrad Meyer 		/*
351fd1efedcSConrad Meyer 		 * If the line starts with a tab, the data has to be
352fd1efedcSConrad Meyer 		 * added to the previous line
353fd1efedcSConrad Meyer 		 */
354fd1efedcSConrad Meyer 		if (buf[0] == '\t') {
355fd1efedcSConrad Meyer 			for (i = 0; i < count; i++)
356fd1efedcSConrad Meyer 				event_continue(events[i], buf);
357fd1efedcSConrad Meyer 			continue;
358fd1efedcSConrad Meyer 		}
359fd1efedcSConrad Meyer 
360fd1efedcSConrad Meyer 		/* Get rid of leading spaces (non-standard) */
361fd1efedcSConrad Meyer 		while (isspace((unsigned char)buf[0]))
362fd1efedcSConrad Meyer 			memcpy(buf, buf + 1, strlen(buf));
363fd1efedcSConrad Meyer 
364fd1efedcSConrad Meyer 		/* No tab in the line, then not a valid line */
365fd1efedcSConrad Meyer 		if ((pp = strchr(buf, '\t')) == NULL)
366fd1efedcSConrad Meyer 			continue;
367fd1efedcSConrad Meyer 
368fd1efedcSConrad Meyer 		/* Trim spaces in front of the tab */
369fd1efedcSConrad Meyer 		while (isspace((unsigned char)pp[-1]))
370fd1efedcSConrad Meyer 			pp--;
371fd1efedcSConrad Meyer 
372fd1efedcSConrad Meyer 		p = *pp;
373fd1efedcSConrad Meyer 		*pp = '\0';
374fd1efedcSConrad Meyer 		if ((count = parsedaymonth(buf, year, month, day, &flags,
375fd1efedcSConrad Meyer 		    extradata)) == 0)
376fd1efedcSConrad Meyer 			continue;
377fd1efedcSConrad Meyer 		*pp = p;
378fd1efedcSConrad Meyer 		if (count < 0) {
379fd1efedcSConrad Meyer 			/* Show error status based on return value */
380fd1efedcSConrad Meyer 			if (debug)
381fd1efedcSConrad Meyer 				fprintf(stderr, "Ignored: %s\n", buf);
382fd1efedcSConrad Meyer 			if (count == -1)
383fd1efedcSConrad Meyer 				continue;
384fd1efedcSConrad Meyer 			count = -count + 1;
385fd1efedcSConrad Meyer 		}
386fd1efedcSConrad Meyer 
387fd1efedcSConrad Meyer 		/* Find the last tab */
388fd1efedcSConrad Meyer 		while (pp[1] == '\t')
389fd1efedcSConrad Meyer 			pp++;
390fd1efedcSConrad Meyer 
391fd1efedcSConrad Meyer 		if (d_first < 0)
392fd1efedcSConrad Meyer 			d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
393fd1efedcSConrad Meyer 
394fd1efedcSConrad Meyer 		for (i = 0; i < count; i++) {
395fd1efedcSConrad Meyer 			tm.tm_mon = month[i] - 1;
396fd1efedcSConrad Meyer 			tm.tm_mday = day[i];
397fd1efedcSConrad Meyer 			tm.tm_year = year[i] - 1900;
398fd1efedcSConrad Meyer 			(void)strftime(dbuf, sizeof(dbuf),
399fd1efedcSConrad Meyer 			    d_first ? "%e %b" : "%b %e", &tm);
400fd1efedcSConrad Meyer 			if (debug)
401fd1efedcSConrad Meyer 				fprintf(stderr, "got %s\n", pp);
402fd1efedcSConrad Meyer 			events[i] = event_add(year[i], month[i], day[i], dbuf,
403fd1efedcSConrad Meyer 			    ((flags &= F_VARIABLE) != 0) ? 1 : 0, pp,
404fd1efedcSConrad Meyer 			    extradata[i]);
405fd1efedcSConrad Meyer 		}
406fd1efedcSConrad Meyer 	}
407fd1efedcSConrad Meyer 
408fd1efedcSConrad Meyer 	free(line);
409fd1efedcSConrad Meyer 	fclose(in);
410fd1efedcSConrad Meyer 
411fd1efedcSConrad Meyer 	return (0);
412fd1efedcSConrad Meyer }
413fd1efedcSConrad Meyer 
414fd1efedcSConrad Meyer void
415fd1efedcSConrad Meyer cal(void)
416fd1efedcSConrad Meyer {
417fd1efedcSConrad Meyer 	FILE *fpin;
418fd1efedcSConrad Meyer 	FILE *fpout;
419fd1efedcSConrad Meyer 	int i;
420fd1efedcSConrad Meyer 
421fd1efedcSConrad Meyer 	for (i = 0; i < MAXCOUNT; i++)
422fd1efedcSConrad Meyer 		extradata[i] = (char *)calloc(1, 20);
423fd1efedcSConrad Meyer 
424fd1efedcSConrad Meyer 
425fd1efedcSConrad Meyer 	if ((fpin = opencalin()) == NULL)
426fd1efedcSConrad Meyer 		return;
427fd1efedcSConrad Meyer 
428fd1efedcSConrad Meyer 	if ((fpout = opencalout()) == NULL) {
429fd1efedcSConrad Meyer 		fclose(fpin);
430fd1efedcSConrad Meyer 		return;
431fd1efedcSConrad Meyer 	}
432fd1efedcSConrad Meyer 
433fd1efedcSConrad Meyer 	if (cal_parse(fpin, fpout))
434fd1efedcSConrad Meyer 		return;
435fd1efedcSConrad Meyer 
436fd1efedcSConrad Meyer 	event_print_all(fpout);
437fd1efedcSConrad Meyer 	closecal(fpout);
438fd1efedcSConrad Meyer }
439fd1efedcSConrad Meyer 
440fd1efedcSConrad Meyer FILE *
441fd1efedcSConrad Meyer opencalin(void)
442fd1efedcSConrad Meyer {
443fd1efedcSConrad Meyer 	struct stat sbuf;
444fd1efedcSConrad Meyer 	FILE *fpin;
445fd1efedcSConrad Meyer 
446fd1efedcSConrad Meyer 	/* open up calendar file */
447fd1efedcSConrad Meyer 	if ((fpin = fopen(calendarFile, "r")) == NULL) {
448fd1efedcSConrad Meyer 		if (doall) {
449fd1efedcSConrad Meyer 			if (chdir(calendarHomes[0]) != 0)
450fd1efedcSConrad Meyer 				return (NULL);
451fd1efedcSConrad Meyer 			if (stat(calendarNoMail, &sbuf) == 0)
452fd1efedcSConrad Meyer 				return (NULL);
453fd1efedcSConrad Meyer 			if ((fpin = fopen(calendarFile, "r")) == NULL)
454fd1efedcSConrad Meyer 				return (NULL);
455fd1efedcSConrad Meyer 		} else {
456fd1efedcSConrad Meyer 			fpin = cal_fopen(calendarFile);
457fd1efedcSConrad Meyer 		}
458fd1efedcSConrad Meyer 	}
459fd1efedcSConrad Meyer 	return (fpin);
460fd1efedcSConrad Meyer }
461fd1efedcSConrad Meyer 
462fd1efedcSConrad Meyer FILE *
463fd1efedcSConrad Meyer opencalout(void)
464fd1efedcSConrad Meyer {
465fd1efedcSConrad Meyer 	int fd;
466fd1efedcSConrad Meyer 
467fd1efedcSConrad Meyer 	/* not reading all calendar files, just set output to stdout */
468fd1efedcSConrad Meyer 	if (!doall)
469fd1efedcSConrad Meyer 		return (stdout);
470fd1efedcSConrad Meyer 
471fd1efedcSConrad Meyer 	/* set output to a temporary file, so if no output don't send mail */
472fd1efedcSConrad Meyer 	snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);
473fd1efedcSConrad Meyer 	if ((fd = mkstemp(path)) < 0)
474fd1efedcSConrad Meyer 		return (NULL);
475fd1efedcSConrad Meyer 	return (fdopen(fd, "w+"));
476fd1efedcSConrad Meyer }
477fd1efedcSConrad Meyer 
478fd1efedcSConrad Meyer void
479fd1efedcSConrad Meyer closecal(FILE *fp)
480fd1efedcSConrad Meyer {
481fd1efedcSConrad Meyer 	struct stat sbuf;
482fd1efedcSConrad Meyer 	int nread, pdes[2], status;
483fd1efedcSConrad Meyer 	char buf[1024];
484fd1efedcSConrad Meyer 
485fd1efedcSConrad Meyer 	if (!doall)
486fd1efedcSConrad Meyer 		return;
487fd1efedcSConrad Meyer 
488fd1efedcSConrad Meyer 	rewind(fp);
489fd1efedcSConrad Meyer 	if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
490fd1efedcSConrad Meyer 		goto done;
491fd1efedcSConrad Meyer 	if (pipe(pdes) < 0)
492fd1efedcSConrad Meyer 		goto done;
493fd1efedcSConrad Meyer 	switch (fork()) {
494fd1efedcSConrad Meyer 	case -1:			/* error */
495fd1efedcSConrad Meyer 		(void)close(pdes[0]);
496fd1efedcSConrad Meyer 		(void)close(pdes[1]);
497fd1efedcSConrad Meyer 		goto done;
498fd1efedcSConrad Meyer 	case 0:
499fd1efedcSConrad Meyer 		/* child -- set stdin to pipe output */
500fd1efedcSConrad Meyer 		if (pdes[0] != STDIN_FILENO) {
501fd1efedcSConrad Meyer 			(void)dup2(pdes[0], STDIN_FILENO);
502fd1efedcSConrad Meyer 			(void)close(pdes[0]);
503fd1efedcSConrad Meyer 		}
504fd1efedcSConrad Meyer 		(void)close(pdes[1]);
505fd1efedcSConrad Meyer 		execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
506fd1efedcSConrad Meyer 		    "\"Reminder Service\"", (char *)NULL);
507fd1efedcSConrad Meyer 		warn(_PATH_SENDMAIL);
508fd1efedcSConrad Meyer 		_exit(1);
509fd1efedcSConrad Meyer 	}
510fd1efedcSConrad Meyer 	/* parent -- write to pipe input */
511fd1efedcSConrad Meyer 	(void)close(pdes[0]);
512fd1efedcSConrad Meyer 
513fd1efedcSConrad Meyer 	write(pdes[1], "From: \"Reminder Service\" <", 26);
514fd1efedcSConrad Meyer 	write(pdes[1], pw->pw_name, strlen(pw->pw_name));
515fd1efedcSConrad Meyer 	write(pdes[1], ">\nTo: <", 7);
516fd1efedcSConrad Meyer 	write(pdes[1], pw->pw_name, strlen(pw->pw_name));
517fd1efedcSConrad Meyer 	write(pdes[1], ">\nSubject: ", 11);
518fd1efedcSConrad Meyer 	write(pdes[1], dayname, strlen(dayname));
519fd1efedcSConrad Meyer 	write(pdes[1], "'s Calendar\nPrecedence: bulk\n\n", 30);
520fd1efedcSConrad Meyer 
521fd1efedcSConrad Meyer 	while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
522fd1efedcSConrad Meyer 		(void)write(pdes[1], buf, nread);
523fd1efedcSConrad Meyer 	(void)close(pdes[1]);
524fd1efedcSConrad Meyer done:	(void)fclose(fp);
525fd1efedcSConrad Meyer 	(void)unlink(path);
526fd1efedcSConrad Meyer 	while (wait(&status) >= 0);
527fd1efedcSConrad Meyer }
528