GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/calendar/io.c Lines: 149 245 60.8 %
Date: 2017-11-07 Branches: 112 219 51.1 %

Line Branch Exec Source
1
/*	$OpenBSD: io.c,v 1.47 2017/09/25 19:13:56 krw 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
13158
	struct event *events, *cur_evt, *ev1, *tmp;
71
6579
	char buf[2048 + 1], *prefix = NULL, *p;
72
	struct match *m;
73
	FILE *fp;
74
75
6579
	events = NULL;
76
	cur_evt = NULL;
77
6579
	if ((fp = opencal()) == NULL)
78
		return;
79
17519877
	for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
80
17506719
		if ((p = strchr(buf, '\n')) != NULL)
81
17506719
			*p = '\0';
82
		else
83
			while ((ch = getchar()) != '\n' && ch != EOF);
84

52520157
		for (l = strlen(buf); l > 0 && isspace(buf[l - 1]); l--)
85
			;
86
17506719
		buf[l] = '\0';
87
17506719
		if (buf[0] == '\0')
88
			continue;
89
17506719
		if (strncmp(buf, "LANG=", 5) == 0) {
90
			(void) setlocale(LC_ALL, buf + 5);
91
			setnnames();
92
			if (!strcmp(buf + 5, "ru_RU.UTF-8") ||
93
			    !strcmp(buf + 5, "uk_UA.UTF-8") ||
94
			    !strcmp(buf + 5, "by_BY.UTF-8")) {
95
				bodun_maybe++;
96
				bodun = 0;
97
				free(prefix);
98
				prefix = NULL;
99
			} else
100
				bodun_maybe = 0;
101
			continue;
102
17506719
		} else if (strncmp(buf, "CALENDAR=", 9) == 0) {
103
			char *ep;
104
105
			if (buf[9] == '\0')
106
				calendar = 0;
107
			else if (!strcasecmp(buf + 9, "julian")) {
108
				calendar = JULIAN;
109
				errno = 0;
110
				julian = strtoul(buf + 14, &ep, 10);
111
				if (buf[0] == '\0' || *ep != '\0')
112
					julian = 13;
113
				if ((errno == ERANGE && julian == ULONG_MAX) ||
114
				    julian > 14)
115
					errx(1, "Julian calendar offset is too large");
116
			} else if (!strcasecmp(buf + 9, "gregorian"))
117
				calendar = GREGORIAN;
118
			else if (!strcasecmp(buf + 9, "lunar"))
119
				calendar = LUNAR;
120

17506719
		} else if (bodun_maybe && strncmp(buf, "BODUN=", 6) == 0) {
121
			bodun++;
122
			free(prefix);
123
			if ((prefix = strdup(buf + 6)) == NULL)
124
				err(1, NULL);
125
			continue;
126
		}
127
		/* User defined names for special events */
128
17506719
		if ((p = strchr(buf, '='))) {
129
39474
			for (i = 0; i < NUMEV; i++) {
130
32895
				if (strncasecmp(buf, spev[i].name,
131
26316
				    spev[i].nlen) == 0 &&
132
6579
				    (p - buf == spev[i].nlen) &&
133
6579
				    buf[spev[i].nlen + 1]) {
134
6579
					p++;
135
6579
					free(spev[i].uname);
136
6579
					if ((spev[i].uname = strdup(p)) == NULL)
137
						err(1, NULL);
138
6579
					spev[i].ulen = strlen(p);
139
					i = NUMEV + 1;
140
6579
				}
141
			}
142
6579
			if (i > NUMEV)
143
				continue;
144
		}
145
17500140
		if (buf[0] != '\t') {
146
15493545
			printing = (m = isnow(buf, bodun)) ? 1 : 0;
147
15493545
			if ((p = strchr(buf, '\t')) == NULL) {
148
				printing = 0;
149
				continue;
150
			}
151
			/* Need the following to catch hardwired "variable"
152
			 * dates */
153

30987090
			if (p > buf && p[-1] == '*')
154
210528
				var = 1;
155
			else
156
				var = 0;
157
15493545
			if (printing) {
158
				struct match *foo;
159
160
				ev1 = NULL;
161
712728
				while (m) {
162
178182
					cur_evt = malloc(sizeof(struct event));
163
178182
					if (cur_evt == NULL)
164
						err(1, NULL);
165
166
178182
					cur_evt->when = m->when;
167
356364
					snprintf(cur_evt->print_date,
168
					    sizeof(cur_evt->print_date), "%s%c",
169
178182
					    m->print_date, (var + m->var) ? '*' : ' ');
170
178182
					if (ev1) {
171
						cur_evt->desc = ev1->desc;
172
						cur_evt->ldesc = NULL;
173
					} else {
174
178182
						if (m->bodun && prefix) {
175
							if (asprintf(&cur_evt->ldesc,
176
							    "\t%s %s", prefix,
177
							    p + 1) == -1)
178
								err(1, NULL);
179
356364
						} else if ((cur_evt->ldesc =
180
356364
						    strdup(p)) == NULL)
181
							err(1, NULL);
182
178182
						cur_evt->desc = &(cur_evt->ldesc);
183
						ev1 = cur_evt;
184
					}
185
178182
					insert(&events, cur_evt);
186
					foo = m;
187
178182
					m = m->next;
188
178182
					free(foo);
189
				}
190
178182
			}
191
2006595
		} else if (printing) {
192
25146
			if (asprintf(&p, "%s\n%s", ev1->ldesc,
193
12573
			    buf) == -1)
194
				err(1, NULL);
195
12573
			free(ev1->ldesc);
196
12573
			ev1->ldesc = p;
197
12573
		}
198
	}
199
6579
	tmp = events;
200
369522
	while (tmp) {
201
178182
		(void)fprintf(fp, "%s%s\n", tmp->print_date, *(tmp->desc));
202
178182
		tmp = tmp->next;
203
	}
204
6579
	tmp = events;
205
369522
	while (tmp) {
206
178182
		events = tmp;
207
178182
		free(tmp->ldesc);
208
178182
		tmp = tmp->next;
209
178182
		free(events);
210
	}
211
6579
	closecal(fp);
212
13158
}
213
214
int
215
getfield(char *p, char **endp, int *flags)
216
{
217
	int val, var, i;
218
	char *start, savech;
219
220

80105904
	for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) &&
221
2131596
	    *p != '*' && *p != '\t'; ++p)
222
		;
223
30967353
	if (*p == '*') {			/* `*' is every month */
224
210528
		*flags |= F_ISMONTH;
225
210528
		*endp = p+1;
226
210528
		return (-1);	/* means 'every month' */
227
	}
228
30756825
	if (isdigit((unsigned char)*p)) {
229
13756689
		val = strtol(p, &p, 10);	/* if 0, it's failure */
230

68987394
		for (; !isdigit((unsigned char)*p) &&
231
51217515
		    !isalpha((unsigned char)*p) && *p != '*'; ++p)
232
			;
233
13756689
		*endp = p;
234
13756689
		return (val);
235
	}
236
102007395
	for (start = p; isalpha((unsigned char)*++p);)
237
		;
238
239
	/* Sunday-1 */
240

24789672
	if (*p == '+' || *p == '-')
241
19908054
		for(; isdigit((unsigned char)*++p); )
242
			;
243
244
17000136
	savech = *p;
245
17000136
	*p = '\0';
246
247
	/* Month */
248
17000136
	if ((val = getmonth(start)) != 0)
249
6855318
		*flags |= F_ISMONTH;
250
251
	/* Day */
252
10144818
	else if ((val = getday(start)) != 0) {
253
9164547
		*flags |= F_ISDAY;
254
255
		/* variable weekday */
256
9164547
		if ((var = getdayvar(start)) != 0) {
257
9118494
			if (var <= 5 && var >= -4)
258
9118494
				val += var * 10;
259
#ifdef DEBUG
260
			printf("var: %d\n", var);
261
#endif
262
		}
263
	}
264
265
	/* Try specials (Easter, Paskha, ...) */
266
	else {
267
7815852
		for (i = 0; i < NUMEV; i++) {
268
2927655
			if (strncasecmp(start, spev[i].name, spev[i].nlen) == 0) {
269
13158
				start += spev[i].nlen;
270
13158
				val = i + 1;
271
				i = NUMEV + 1;
272

3072393
			} else if (spev[i].uname != NULL &&
273
144738
			    strncasecmp(start, spev[i].uname, spev[i].ulen) == 0) {
274
6579
				start += spev[i].ulen;
275
6579
				val = i + 1;
276
				i = NUMEV + 1;
277
6579
			}
278
		}
279
980271
		if (i > NUMEV) {
280
26316
			const char *errstr;
281
282
26316
			switch (*start) {
283
			case '-':
284
			case '+':
285
6579
				var = strtonum(start + 1, 0, 365, &errstr);
286
6579
				if (errstr)
287
					return (0); /* Someone is just being silly */
288
6579
				if (*start == '-')
289
6579
					var = -var;
290
6579
				val += (NUMEV + 1) * var;
291
				/* We add one to the matching event and multiply by
292
				 * (NUMEV + 1) so as not to return 0 if there's a match.
293
				 * val will overflow if there is an obscenely large
294
				 * number of special events. */
295
6579
				break;
296
			}
297
19737
			*flags |= F_SPECIAL;
298
39474
		}
299
980271
		if (!(*flags & F_SPECIAL)) {
300
			/* undefined rest */
301
960534
			*p = savech;
302
960534
			return (0);
303
		}
304
	}
305

63678141
	for (*p = savech; !isdigit((unsigned char)*p) &&
306

63678141
	    !isalpha((unsigned char)*p) && *p != '*' && *p != '\t'; ++p)
307
		;
308
16039602
	*endp = p;
309
16039602
	return (val);
310
30967353
}
311
312
313
FILE *
314
opencal(void)
315
{
316
13158
	int pdes[2], fdin;
317
6579
	struct stat st;
318
319
	/* open up calendar file as stdin */
320

13158
	if ((fdin = open(calendarFile, O_RDONLY)) == -1 ||
321
13158
	    fstat(fdin, &st) == -1 || !S_ISREG(st.st_mode)) {
322
6579
		if (!doall) {
323
			char *home = getenv("HOME");
324
			if (home == NULL || *home == '\0')
325
				errx(1, "cannot get home directory");
326
			if (!(chdir(home) == 0 &&
327
			    chdir(calendarHome) == 0 &&
328
			    (fdin = open(calendarFile, O_RDONLY)) != -1))
329
				errx(1, "no calendar file: ``%s'' or ``~/%s/%s''",
330
				    calendarFile, calendarHome, calendarFile);
331
		}
332
	}
333
334
6579
	if (pipe(pdes) < 0) {
335
		close(fdin);
336
		return (NULL);
337
	}
338
6579
	switch (vfork()) {
339
	case -1:			/* error */
340
		(void)close(pdes[0]);
341
		(void)close(pdes[1]);
342
		close(fdin);
343
		return (NULL);
344
	case 0:
345
		dup2(fdin, STDIN_FILENO);
346
		/* child -- set stdout to pipe input */
347
		if (pdes[1] != STDOUT_FILENO) {
348
			(void)dup2(pdes[1], STDOUT_FILENO);
349
			(void)close(pdes[1]);
350
		}
351
		(void)close(pdes[0]);
352
		/*
353
		 * Set stderr to /dev/null.  Necessary so that cron does not
354
		 * wait for cpp to finish if it's running calendar -a.
355
		 */
356
		if (doall) {
357
			int fderr;
358
			fderr = open(_PATH_DEVNULL, O_WRONLY, 0);
359
			if (fderr == -1)
360
				_exit(0);
361
			(void)dup2(fderr, STDERR_FILENO);
362
			(void)close(fderr);
363
		}
364
		execl(_PATH_CPP, "cpp", "-traditional", "-undef", "-U__GNUC__",
365
		    "-P", "-I.", _PATH_INCLUDE, (char *)NULL);
366
		warn(_PATH_CPP);
367
		_exit(1);
368
	}
369
	/* parent -- set stdin to pipe output */
370
6579
	(void)dup2(pdes[0], STDIN_FILENO);
371
6579
	(void)close(pdes[0]);
372
6579
	(void)close(pdes[1]);
373
374
	/* not reading all calendar files, just set output to stdout */
375
6579
	if (!doall)
376
6579
		return (stdout);
377
378
	/* set output to a temporary file, so if no output don't send mail */
379
	return(tmpfile());
380
6579
}
381
382
void
383
closecal(FILE *fp)
384
{
385
13158
	struct stat sbuf;
386
6579
	int nread, pdes[2], status;
387
6579
	char buf[1024];
388
	pid_t pid = -1;
389
390
6579
	if (!doall)
391
6579
		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 ((pid = 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
	if (pid != -1) {
426
		while (waitpid(pid, &status, 0) == -1) {
427
			if (errno != EINTR)
428
				break;
429
		}
430
	}
431
6579
}
432
433
434
void
435
insert(struct event **head, struct event *cur_evt)
436
{
437
	struct event *tmp, *tmp2;
438
439
356364
	if (*head) {
440
		/* Insert this one in order */
441
		tmp = *head;
442
		tmp2 = NULL;
443

5930712
		while (tmp->next &&
444
1884582
		    tmp->when <= cur_evt->when) {
445
			tmp2 = tmp;
446
1805301
			tmp = tmp->next;
447
		}
448
171603
		if (tmp->when > cur_evt->when) {
449
79281
			cur_evt->next = tmp;
450
158562
			if (tmp2)
451
79281
				tmp2->next = cur_evt;
452
			else
453
				*head = cur_evt;
454
79281
		} else {
455
92322
			cur_evt->next = tmp->next;
456
92322
			tmp->next = cur_evt;
457
		}
458
	} else {
459
6579
		*head = cur_evt;
460
6579
		cur_evt->next = NULL;
461
	}
462
178182
}