GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/calendar/io.c Lines: 0 223 0.0 %
Date: 2016-12-06 Branches: 0 226 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: io.c,v 1.43 2015/12/08 19:04:50 mmcc Exp $	*/
2
3
/*
4
 * Copyright (c) 1989, 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. Neither the name of the University nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
#include <sys/stat.h>
33
#include <sys/time.h>
34
#include <sys/types.h>
35
#include <sys/uio.h>
36
#include <sys/wait.h>
37
38
#include <ctype.h>
39
#include <err.h>
40
#include <errno.h>
41
#include <fcntl.h>
42
#include <locale.h>
43
#include <pwd.h>
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include <string.h>
47
#include <unistd.h>
48
#include <limits.h>
49
50
#include "pathnames.h"
51
#include "calendar.h"
52
53
54
struct iovec header[] = {
55
	{ "From: ", 6 },
56
	{ NULL, 0 },
57
	{ " (Reminder Service)\nTo: ", 24 },
58
	{ NULL, 0 },
59
	{ "\nSubject: ", 10 },
60
	{ NULL, 0 },
61
	{ "'s Calendar\nPrecedence: bulk\n",  29 },
62
	{ "Auto-Submitted: auto-generated\n\n", 32 },
63
};
64
65
66
void
67
cal(void)
68
{
69
	int ch, l, i, bodun = 0, bodun_maybe = 0, var, printing;
70
	struct event *events, *cur_evt, *ev1, *tmp;
71
	char buf[2048 + 1], *prefix = NULL, *p;
72
	struct match *m;
73
	FILE *fp;
74
75
	events = NULL;
76
	cur_evt = NULL;
77
	if ((fp = opencal()) == NULL)
78
		return;
79
	for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
80
		if ((p = strchr(buf, '\n')) != NULL)
81
			*p = '\0';
82
		else
83
			while ((ch = getchar()) != '\n' && ch != EOF);
84
		for (l = strlen(buf); l > 0 && isspace(buf[l - 1]); l--)
85
			;
86
		buf[l] = '\0';
87
		if (buf[0] == '\0')
88
			continue;
89
		if (strncmp(buf, "LANG=", 5) == 0) {
90
			(void) setlocale(LC_ALL, buf + 5);
91
			setnnames();
92
			/* XXX remove KOI8 lines after 5.9 is out */
93
			if (!strcmp(buf + 5, "ru_RU.UTF-8") ||
94
			    !strcmp(buf + 5, "uk_UA.UTF-8") ||
95
			    !strcmp(buf + 5, "by_BY.UTF-8") ||
96
			    !strcmp(buf + 5, "ru_RU.KOI8-R") ||
97
			    !strcmp(buf + 5, "uk_UA.KOI8-U") ||
98
			    !strcmp(buf + 5, "by_BY.KOI8-B")) {
99
				bodun_maybe++;
100
				bodun = 0;
101
				free(prefix);
102
				prefix = NULL;
103
			} else
104
				bodun_maybe = 0;
105
			continue;
106
		} else if (strncmp(buf, "CALENDAR=", 9) == 0) {
107
			char *ep;
108
109
			if (buf[9] == '\0')
110
				calendar = 0;
111
			else if (!strcasecmp(buf + 9, "julian")) {
112
				calendar = JULIAN;
113
				errno = 0;
114
				julian = strtoul(buf + 14, &ep, 10);
115
				if (buf[0] == '\0' || *ep != '\0')
116
					julian = 13;
117
				if ((errno == ERANGE && julian == ULONG_MAX) ||
118
				    julian > 14)
119
					errx(1, "Julian calendar offset is too large");
120
			} else if (!strcasecmp(buf + 9, "gregorian"))
121
				calendar = GREGORIAN;
122
			else if (!strcasecmp(buf + 9, "lunar"))
123
				calendar = LUNAR;
124
		} else if (bodun_maybe && strncmp(buf, "BODUN=", 6) == 0) {
125
			bodun++;
126
			free(prefix);
127
			if ((prefix = strdup(buf + 6)) == NULL)
128
				err(1, NULL);
129
			continue;
130
		}
131
		/* User defined names for special events */
132
		if ((p = strchr(buf, '='))) {
133
			for (i = 0; i < NUMEV; i++) {
134
				if (strncasecmp(buf, spev[i].name,
135
				    spev[i].nlen) == 0 &&
136
				    (p - buf == spev[i].nlen) &&
137
				    buf[spev[i].nlen + 1]) {
138
					p++;
139
					free(spev[i].uname);
140
					if ((spev[i].uname = strdup(p)) == NULL)
141
						err(1, NULL);
142
					spev[i].ulen = strlen(p);
143
					i = NUMEV + 1;
144
				}
145
			}
146
			if (i > NUMEV)
147
				continue;
148
		}
149
		if (buf[0] != '\t') {
150
			printing = (m = isnow(buf, bodun)) ? 1 : 0;
151
			if ((p = strchr(buf, '\t')) == NULL) {
152
				printing = 0;
153
				continue;
154
			}
155
			/* Need the following to catch hardwired "variable"
156
			 * dates */
157
			if (p > buf && p[-1] == '*')
158
				var = 1;
159
			else
160
				var = 0;
161
			if (printing) {
162
				struct match *foo;
163
164
				ev1 = NULL;
165
				while (m) {
166
					cur_evt = malloc(sizeof(struct event));
167
					if (cur_evt == NULL)
168
						err(1, NULL);
169
170
					cur_evt->when = m->when;
171
					snprintf(cur_evt->print_date,
172
					    sizeof(cur_evt->print_date), "%s%c",
173
					    m->print_date, (var + m->var) ? '*' : ' ');
174
					if (ev1) {
175
						cur_evt->desc = ev1->desc;
176
						cur_evt->ldesc = NULL;
177
					} else {
178
						if (m->bodun && prefix) {
179
							if (asprintf(&cur_evt->ldesc,
180
							    "\t%s %s", prefix,
181
							    p + 1) == -1)
182
								err(1, NULL);
183
						} else if ((cur_evt->ldesc =
184
						    strdup(p)) == NULL)
185
							err(1, NULL);
186
						cur_evt->desc = &(cur_evt->ldesc);
187
						ev1 = cur_evt;
188
					}
189
					insert(&events, cur_evt);
190
					foo = m;
191
					m = m->next;
192
					free(foo);
193
				}
194
			}
195
		} else if (printing) {
196
			if (asprintf(&p, "%s\n%s", ev1->ldesc,
197
			    buf) == -1)
198
				err(1, NULL);
199
			free(ev1->ldesc);
200
			ev1->ldesc = p;
201
		}
202
	}
203
	tmp = events;
204
	while (tmp) {
205
		(void)fprintf(fp, "%s%s\n", tmp->print_date, *(tmp->desc));
206
		tmp = tmp->next;
207
	}
208
	tmp = events;
209
	while (tmp) {
210
		events = tmp;
211
		free(tmp->ldesc);
212
		tmp = tmp->next;
213
		free(events);
214
	}
215
	closecal(fp);
216
}
217
218
int
219
getfield(char *p, char **endp, int *flags)
220
{
221
	int val, var, i;
222
	char *start, savech;
223
224
	for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) &&
225
	    *p != '*' && *p != '\t'; ++p)
226
		;
227
	if (*p == '*') {			/* `*' is every month */
228
		*flags |= F_ISMONTH;
229
		*endp = p+1;
230
		return (-1);	/* means 'every month' */
231
	}
232
	if (isdigit((unsigned char)*p)) {
233
		val = strtol(p, &p, 10);	/* if 0, it's failure */
234
		for (; !isdigit((unsigned char)*p) &&
235
		    !isalpha((unsigned char)*p) && *p != '*'; ++p)
236
			;
237
		*endp = p;
238
		return (val);
239
	}
240
	for (start = p; isalpha((unsigned char)*++p);)
241
		;
242
243
	/* Sunday-1 */
244
	if (*p == '+' || *p == '-')
245
		for(; isdigit((unsigned char)*++p); )
246
			;
247
248
	savech = *p;
249
	*p = '\0';
250
251
	/* Month */
252
	if ((val = getmonth(start)) != 0)
253
		*flags |= F_ISMONTH;
254
255
	/* Day */
256
	else if ((val = getday(start)) != 0) {
257
		*flags |= F_ISDAY;
258
259
		/* variable weekday */
260
		if ((var = getdayvar(start)) != 0) {
261
			if (var <= 5 && var >= -4)
262
				val += var * 10;
263
#ifdef DEBUG
264
			printf("var: %d\n", var);
265
#endif
266
		}
267
	}
268
269
	/* Try specials (Easter, Paskha, ...) */
270
	else {
271
		for (i = 0; i < NUMEV; i++) {
272
			if (strncasecmp(start, spev[i].name, spev[i].nlen) == 0) {
273
				start += spev[i].nlen;
274
				val = i + 1;
275
				i = NUMEV + 1;
276
			} else if (spev[i].uname != NULL &&
277
			    strncasecmp(start, spev[i].uname, spev[i].ulen) == 0) {
278
				start += spev[i].ulen;
279
				val = i + 1;
280
				i = NUMEV + 1;
281
			}
282
		}
283
		if (i > NUMEV) {
284
			const char *errstr;
285
286
			switch (*start) {
287
			case '-':
288
			case '+':
289
				var = strtonum(start + 1, 0, 365, &errstr);
290
				if (errstr)
291
					return (0); /* Someone is just being silly */
292
				if (*start == '-')
293
					var = -var;
294
				val += (NUMEV + 1) * var;
295
				/* We add one to the matching event and multiply by
296
				 * (NUMEV + 1) so as not to return 0 if there's a match.
297
				 * val will overflow if there is an obscenely large
298
				 * number of special events. */
299
				break;
300
			}
301
			*flags |= F_SPECIAL;
302
		}
303
		if (!(*flags & F_SPECIAL)) {
304
			/* undefined rest */
305
			*p = savech;
306
			return (0);
307
		}
308
	}
309
	for (*p = savech; !isdigit((unsigned char)*p) &&
310
	    !isalpha((unsigned char)*p) && *p != '*' && *p != '\t'; ++p)
311
		;
312
	*endp = p;
313
	return (val);
314
}
315
316
317
FILE *
318
opencal(void)
319
{
320
	int pdes[2], fdin;
321
	struct stat st;
322
323
	/* open up calendar file as stdin */
324
	if ((fdin = open(calendarFile, O_RDONLY)) == -1 ||
325
	    fstat(fdin, &st) == -1 || !S_ISREG(st.st_mode)) {
326
		if (!doall) {
327
			char *home = getenv("HOME");
328
			if (home == NULL || *home == '\0')
329
				errx(1, "cannot get home directory");
330
			if (!(chdir(home) == 0 &&
331
			    chdir(calendarHome) == 0 &&
332
			    (fdin = open(calendarFile, O_RDONLY)) != -1))
333
				errx(1, "no calendar file: ``%s'' or ``~/%s/%s''",
334
				    calendarFile, calendarHome, calendarFile);
335
		}
336
	}
337
338
	if (pipe(pdes) < 0)
339
		return (NULL);
340
	switch (vfork()) {
341
	case -1:			/* error */
342
		(void)close(pdes[0]);
343
		(void)close(pdes[1]);
344
		return (NULL);
345
	case 0:
346
		dup2(fdin, STDIN_FILENO);
347
		/* child -- set stdout to pipe input */
348
		if (pdes[1] != STDOUT_FILENO) {
349
			(void)dup2(pdes[1], STDOUT_FILENO);
350
			(void)close(pdes[1]);
351
		}
352
		(void)close(pdes[0]);
353
		/*
354
		 * Set stderr to /dev/null.  Necessary so that cron does not
355
		 * wait for cpp to finish if it's running calendar -a.
356
		 */
357
		if (doall) {
358
			int fderr;
359
			fderr = open(_PATH_DEVNULL, O_WRONLY, 0);
360
			if (fderr == -1)
361
				_exit(0);
362
			(void)dup2(fderr, STDERR_FILENO);
363
			(void)close(fderr);
364
		}
365
		execl(_PATH_CPP, "cpp", "-traditional", "-undef", "-U__GNUC__",
366
		    "-P", "-I.", _PATH_INCLUDE, (char *)NULL);
367
		warn(_PATH_CPP);
368
		_exit(1);
369
	}
370
	/* parent -- set stdin to pipe output */
371
	(void)dup2(pdes[0], STDIN_FILENO);
372
	(void)close(pdes[0]);
373
	(void)close(pdes[1]);
374
375
	/* not reading all calendar files, just set output to stdout */
376
	if (!doall)
377
		return (stdout);
378
379
	/* set output to a temporary file, so if no output don't send mail */
380
	return(tmpfile());
381
}
382
383
void
384
closecal(FILE *fp)
385
{
386
	struct stat sbuf;
387
	int nread, pdes[2], status;
388
	char buf[1024];
389
390
	if (!doall)
391
		return;
392
393
	(void)rewind(fp);
394
	if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
395
		goto done;
396
	if (pipe(pdes) < 0)
397
		goto done;
398
	switch (vfork()) {
399
	case -1:			/* error */
400
		(void)close(pdes[0]);
401
		(void)close(pdes[1]);
402
		goto done;
403
	case 0:
404
		/* child -- set stdin to pipe output */
405
		if (pdes[0] != STDIN_FILENO) {
406
			(void)dup2(pdes[0], STDIN_FILENO);
407
			(void)close(pdes[0]);
408
		}
409
		(void)close(pdes[1]);
410
		execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
411
		    "\"Reminder Service\"", (char *)NULL);
412
		warn(_PATH_SENDMAIL);
413
		_exit(1);
414
	}
415
	/* parent -- write to pipe input */
416
	(void)close(pdes[0]);
417
418
	header[1].iov_base = header[3].iov_base = pw->pw_name;
419
	header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
420
	writev(pdes[1], header, 8);
421
	while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
422
		(void)write(pdes[1], buf, nread);
423
	(void)close(pdes[1]);
424
done:	(void)fclose(fp);
425
	while (wait(&status) >= 0)
426
		;
427
}
428
429
430
void
431
insert(struct event **head, struct event *cur_evt)
432
{
433
	struct event *tmp, *tmp2;
434
435
	if (*head) {
436
		/* Insert this one in order */
437
		tmp = *head;
438
		tmp2 = NULL;
439
		while (tmp->next &&
440
		    tmp->when <= cur_evt->when) {
441
			tmp2 = tmp;
442
			tmp = tmp->next;
443
		}
444
		if (tmp->when > cur_evt->when) {
445
			cur_evt->next = tmp;
446
			if (tmp2)
447
				tmp2->next = cur_evt;
448
			else
449
				*head = cur_evt;
450
		} else {
451
			cur_evt->next = tmp->next;
452
			tmp->next = cur_evt;
453
		}
454
	} else {
455
		*head = cur_evt;
456
		cur_evt->next = NULL;
457
	}
458
}