GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/calendar/io.c Lines: 150 249 60.2 %
Date: 2017-11-13 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
7310
	struct event *events, *cur_evt, *ev1, *tmp;
71
3655
	char buf[2048 + 1], *prefix = NULL, *p;
72
	struct match *m;
73
	FILE *fp;
74
75
3655
	events = NULL;
76
	cur_evt = NULL;
77
3655
	if ((fp = opencal()) == NULL)
78
		return;
79
19455565
	for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
80
9725955
		if ((p = strchr(buf, '\n')) != NULL)
81
9725955
			*p = '\0';
82
		else
83
			while ((ch = getchar()) != '\n' && ch != EOF);
84

29177865
		for (l = strlen(buf); l > 0 && isspace(buf[l - 1]); l--)
85
			;
86
9725955
		buf[l] = '\0';
87
9725955
		if (buf[0] == '\0')
88
			continue;
89
9725955
		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
9725955
		} 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

9725955
		} 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
9725955
		if ((p = strchr(buf, '='))) {
129
21930
			for (i = 0; i < NUMEV; i++) {
130
18275
				if (strncasecmp(buf, spev[i].name,
131
14620
				    spev[i].nlen) == 0 &&
132
3655
				    (p - buf == spev[i].nlen) &&
133
3655
				    buf[spev[i].nlen + 1]) {
134
3655
					p++;
135
3655
					free(spev[i].uname);
136
3655
					if ((spev[i].uname = strdup(p)) == NULL)
137
						err(1, NULL);
138
3655
					spev[i].ulen = strlen(p);
139
					i = NUMEV + 1;
140
3655
				}
141
			}
142
3655
			if (i > NUMEV)
143
3655
				continue;
144
		}
145
9722300
		if (buf[0] != '\t') {
146
8607525
			printing = (m = isnow(buf, bodun)) ? 1 : 0;
147
8607525
			if ((p = strchr(buf, '\t')) == NULL) {
148
				printing = 0;
149
				continue;
150
			}
151
			/* Need the following to catch hardwired "variable"
152
			 * dates */
153

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

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

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

13772040
	if (*p == '+' || *p == '-')
241
22120060
		for(; isdigit((unsigned char)*++p); )
242
			;
243
244
9444520
	savech = *p;
245
9444520
	*p = '\0';
246
247
	/* Month */
248
9444520
	if ((val = getmonth(start)) != 0)
249
3808510
		*flags |= F_ISMONTH;
250
251
	/* Day */
252
5636010
	else if ((val = getday(start)) != 0) {
253
5091415
		*flags |= F_ISDAY;
254
255
		/* variable weekday */
256
5091415
		if ((var = getdayvar(start)) != 0) {
257
5065830
			if (var <= 5 && var >= -4)
258
5065830
				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
4342140
		for (i = 0; i < NUMEV; i++) {
268
1626475
			if (strncasecmp(start, spev[i].name, spev[i].nlen) == 0) {
269
7310
				start += spev[i].nlen;
270
7310
				val = i + 1;
271
				i = NUMEV + 1;
272

1706885
			} else if (spev[i].uname != NULL &&
273
80410
			    strncasecmp(start, spev[i].uname, spev[i].ulen) == 0) {
274
3655
				start += spev[i].ulen;
275
3655
				val = i + 1;
276
				i = NUMEV + 1;
277
3655
			}
278
		}
279
544595
		if (i > NUMEV) {
280
14620
			const char *errstr;
281
282
14620
			switch (*start) {
283
			case '-':
284
			case '+':
285
3655
				var = strtonum(start + 1, 0, 365, &errstr);
286
3655
				if (errstr)
287
					return (0); /* Someone is just being silly */
288
3655
				if (*start == '-')
289
3655
					var = -var;
290
3655
				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
3655
				break;
296
			}
297
10965
			*flags |= F_SPECIAL;
298
21930
		}
299
544595
		if (!(*flags & F_SPECIAL)) {
300
			/* undefined rest */
301
533630
			*p = savech;
302
533630
			return (0);
303
		}
304
	}
305

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

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

7310
	if ((fdin = open(calendarFile, O_RDONLY)) == -1 ||
321
7310
	    fstat(fdin, &st) == -1 || !S_ISREG(st.st_mode)) {
322
3655
		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
3655
	if (pipe(pdes) < 0) {
335
		close(fdin);
336
		return (NULL);
337
	}
338
3655
	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
3655
	(void)dup2(pdes[0], STDIN_FILENO);
371
3655
	(void)close(pdes[0]);
372
3655
	(void)close(pdes[1]);
373
374
	/* not reading all calendar files, just set output to stdout */
375
3655
	if (!doall)
376
3655
		return (stdout);
377
378
	/* set output to a temporary file, so if no output don't send mail */
379
	return(tmpfile());
380
3655
}
381
382
void
383
closecal(FILE *fp)
384
{
385
7310
	struct stat sbuf;
386
3655
	int nread, pdes[2], status;
387
3655
	char buf[1024];
388
	pid_t pid = -1;
389
390
3655
	if (!doall)
391
3655
		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
3655
}
432
433
434
void
435
insert(struct event **head, struct event *cur_evt)
436
{
437
	struct event *tmp, *tmp2;
438
439
197980
	if (*head) {
440
		/* Insert this one in order */
441
		tmp = *head;
442
		tmp2 = NULL;
443

3294840
		while (tmp->next &&
444
1046990
		    tmp->when <= cur_evt->when) {
445
			tmp2 = tmp;
446
1002945
			tmp = tmp->next;
447
		}
448
95335
		if (tmp->when > cur_evt->when) {
449
44045
			cur_evt->next = tmp;
450
88090
			if (tmp2)
451
44045
				tmp2->next = cur_evt;
452
			else
453
				*head = cur_evt;
454
44045
		} else {
455
51290
			cur_evt->next = tmp->next;
456
51290
			tmp->next = cur_evt;
457
		}
458
	} else {
459
3655
		*head = cur_evt;
460
3655
		cur_evt->next = NULL;
461
	}
462
98990
}