Bring in an errno.9 manual page (based on NetBSD's).
[dragonfly.git] / usr.bin / calendar / io.c
blob2a738c5059cbe33dd5558909ef956bbe09ed323e
1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
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 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
29 * @(#) Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved.
30 * @(#)calendar.c 8.3 (Berkeley) 3/25/94
31 * $FreeBSD: src/usr.bin/calendar/io.c,v 1.21 2007/06/09 05:54:13 grog Exp $
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <sys/uio.h>
38 #include <sys/wait.h>
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <langinfo.h>
44 #include <locale.h>
45 #include <pwd.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
51 #include "pathnames.h"
52 #include "calendar.h"
54 const char *calendarFile = "calendar.all"; /* default calendar file */
55 const char *calendarHomes[] = { ".calendar", _PATH_INCLUDE }; /* HOME */
56 const char *calendarNoMail = "nomail"; /* don't send mail if this file exist */
58 struct fixs neaster, npaskha;
60 char hdr_from[] = "From: ";
61 char hdr_to[] = " (Reminder Service)\nTo: ";
62 char hdr_subj[] = "\nSubject: ";
63 char hdr_prec[] = "'s Calendar\nPrecedence: bulk\n\n";
65 struct iovec header[] = {
66 {hdr_from, 6},
67 {NULL, 0},
68 {hdr_to, 24},
69 {NULL, 0},
70 {hdr_subj, 10},
71 {NULL, 0},
72 {hdr_prec, 30},
75 static FILE *opencal(void);
76 static void closecal(FILE *);
78 void
79 cal(void)
81 int printing;
82 char *p;
83 FILE *fp;
84 int ch, l;
85 int month;
86 int day;
87 int var = 0; /* avoid gcc warnings */
88 static int d_first = -1;
89 char buf[2048 + 1];
90 struct event *events = NULL;
92 if ((fp = opencal()) == NULL)
93 return;
94 for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
95 if ((p = strchr(buf, '\n')) != NULL)
96 *p = '\0';
97 else
98 while ((ch = getchar()) != '\n' && ch != EOF);
99 for (l = strlen(buf);
100 l > 0 && isspace((unsigned char)buf[l - 1]);
101 l--)
103 buf[l] = '\0';
104 if (buf[0] == '\0')
105 continue;
106 if (strncmp(buf, "LANG=", 5) == 0) {
107 setlocale(LC_ALL, buf + 5);
108 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
109 setnnames();
110 continue;
112 if (strncasecmp(buf, "Easter=", 7) == 0 && buf[7]) {
113 if (neaster.name != NULL)
114 free(neaster.name);
115 if ((neaster.name = strdup(buf + 7)) == NULL)
116 errx(EXIT_FAILURE, "cannot allocate memory");
117 neaster.len = strlen(buf + 7);
118 continue;
120 if (strncasecmp(buf, "Paskha=", 7) == 0 && buf[7]) {
121 if (npaskha.name != NULL)
122 free(npaskha.name);
123 if ((npaskha.name = strdup(buf + 7)) == NULL)
124 errx(EXIT_FAILURE, "cannot allocate memory");
125 npaskha.len = strlen(buf + 7);
126 continue;
128 if (buf[0] != '\t') {
129 printing = isnow(buf, &month, &day, &var) ? 1 : 0;
130 if ((p = strchr(buf, '\t')) == NULL)
131 continue;
132 if (p > buf && p[-1] == '*')
133 var = 1;
134 if (printing) {
135 struct tm tm;
136 char dbuf[80];
138 if (d_first < 0)
139 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
140 tm.tm_sec = 0; /* unused */
141 tm.tm_min = 0; /* unused */
142 tm.tm_hour = 0; /* unused */
143 tm.tm_wday = 0; /* unused */
144 tm.tm_mon = month - 1;
145 tm.tm_mday = day;
146 tm.tm_year = tp->tm_year; /* unused */
147 strftime(dbuf, sizeof(dbuf),
148 d_first ? "%e %b" : "%b %e",
149 &tm);
150 events = event_add(events, month, day, dbuf, var, p);
153 else if (printing)
154 event_continue(events, buf);
157 event_print_all(fp, events);
158 closecal(fp);
162 * Functions to handle buffered calendar events.
164 struct event *
165 event_add(struct event *events, int month, int day, char *date, int var, char *txt)
167 struct event *e;
169 e = (struct event *)calloc(1, sizeof(struct event));
170 if (e == NULL)
171 errx(EXIT_FAILURE, "event_add: cannot allocate memory");
172 e->month = month;
173 e->day = day;
174 e->var = var;
175 e->date = strdup(date);
176 if (e->date == NULL)
177 errx(EXIT_FAILURE, "event_add: cannot allocate memory");
178 e->text = strdup(txt);
179 if (e->text == NULL)
180 errx(EXIT_FAILURE, "event_add: cannot allocate memory");
181 e->next = events;
183 return e;
186 void
187 event_continue(struct event *e, char *txt)
189 char *text;
191 text = strdup(e->text);
192 if (text == NULL)
193 errx(EXIT_FAILURE, "event_continue: cannot allocate memory");
195 free(e->text);
196 e->text = (char *)malloc(strlen(text) + strlen(txt) + 3);
197 if (e->text == NULL)
198 errx(EXIT_FAILURE, "event_continue: cannot allocate memory");
199 strcpy(e->text, text);
200 strcat(e->text, "\n");
201 strcat(e->text, txt);
202 free(text);
204 return;
207 void
208 event_print_all(FILE *fp, struct event *events)
210 struct event *e, *e_next;
211 int daycount = f_dayAfter + f_dayBefore;
212 int daycounter;
213 int day, month;
215 for (daycounter = 0; daycounter <= daycount; daycounter++) {
216 day = tp->tm_yday - f_dayBefore + daycounter;
217 if (day < 0) day += yrdays;
218 if (day >= yrdays) day -= yrdays;
220 month = 1;
221 while (month <= 12) {
222 if (day <= cumdays[month])
223 break;
224 month++;
226 month--;
227 day -= cumdays[month];
229 #ifdef DEBUG
230 fprintf(stderr,"event_print_allmonth: %d, day: %d\n",month,day);
231 #endif
233 for (e = events; e != NULL; e = e_next ) {
234 e_next = e->next;
236 if (month != e->month || day != e->day)
237 continue;
239 fprintf(fp, "%s%c%s\n", e->date,
240 e->var ? '*' : ' ', e->text);
246 getfield(char *p, char **endp, int *flags)
248 int val, var;
249 char *start, savech;
251 for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p)
252 && *p != '*'; ++p);
253 if (*p == '*') { /* `*' is current month */
254 *flags |= F_ISMONTH;
255 *endp = p+1;
256 return(tp->tm_mon + 1);
258 if (isdigit((unsigned char)*p)) {
259 val = strtol(p, &p, 10); /* if 0, it's failure */
260 for (; !isdigit((unsigned char)*p)
261 && !isalpha((unsigned char)*p) && *p != '*'; ++p);
262 *endp = p;
263 return(val);
265 for (start = p; isalpha((unsigned char)*++p););
267 /* Sunday-1 */
268 if (*p == '+' || *p == '-')
269 for(; isdigit((unsigned char)*++p););
271 savech = *p;
272 *p = '\0';
274 /* Month */
275 if ((val = getmonth(start)) != 0)
276 *flags |= F_ISMONTH;
278 /* Day */
279 else if ((val = getday(start)) != 0) {
280 *flags |= F_ISDAY;
282 /* variable weekday */
283 if ((var = getdayvar(start)) != 0) {
284 if (var <=5 && var >= -4)
285 val += var * 10;
286 #ifdef DEBUG
287 printf("var: %d\n", var);
288 #endif
292 /* Easter */
293 else if ((val = geteaster(start, tp->tm_year + 1900)) != 0)
294 *flags |= F_EASTER;
296 /* Paskha */
297 else if ((val = getpaskha(start, tp->tm_year + 1900)) != 0)
298 *flags |= F_EASTER;
300 /* undefined rest */
301 else {
302 *p = savech;
303 return (0);
305 for (*p = savech; !isdigit((unsigned char)*p)
306 && !isalpha((unsigned char)*p) && *p != '*'; ++p);
307 *endp = p;
308 return(val);
311 static char path[MAXPATHLEN];
313 static FILE *
314 opencal(void)
316 uid_t uid;
317 size_t i;
318 int fd, found, pdes[2];
319 struct stat sbuf;
321 /* open up calendar file as stdin */
322 if (!freopen(calendarFile, "r", stdin)) {
323 if (doall) {
324 if (chdir(calendarHomes[0]) != 0)
325 return(NULL);
326 if (stat(calendarNoMail, &sbuf) == 0)
327 return(NULL);
328 if (!freopen(calendarFile, "r", stdin))
329 return(NULL);
330 } else {
331 chdir(getenv("HOME"));
332 for (found = i = 0; i < sizeof(calendarHomes) /
333 sizeof(calendarHomes[0]); i++)
334 if (chdir(calendarHomes[i]) == 0 &&
335 freopen(calendarFile, "r", stdin)) {
336 found = 1;
337 break;
339 if (!found)
340 errx(EXIT_FAILURE, "no calendar file: ``%s''", calendarFile);
343 if (pipe(pdes) < 0)
344 return(NULL);
345 switch (fork()) {
346 case -1: /* error */
347 close(pdes[0]);
348 close(pdes[1]);
349 return(NULL);
350 case 0:
351 /* child -- stdin already setup, set stdout to pipe input */
352 if (pdes[1] != STDOUT_FILENO) {
353 dup2(pdes[1], STDOUT_FILENO);
354 close(pdes[1]);
356 close(pdes[0]);
357 uid = geteuid();
358 if (setuid(getuid()) < 0) {
359 warnx("first setuid failed");
360 _exit(EXIT_FAILURE);
362 if (setgid(getegid()) < 0) {
363 warnx("setgid failed");
364 _exit(EXIT_FAILURE);
366 if (setuid(uid) < 0) {
367 warnx("setuid failed");
368 _exit(EXIT_FAILURE);
370 execl(_PATH_CPP, "cpp", "-P",
371 "-traditional", "-nostdinc", /* GCC specific opts */
372 "-I.", "-I", _PATH_INCLUDE, NULL);
373 warn(_PATH_CPP);
374 _exit(EXIT_FAILURE);
376 /* parent -- set stdin to pipe output */
377 dup2(pdes[0], STDIN_FILENO);
378 close(pdes[0]);
379 close(pdes[1]);
381 /* not reading all calendar files, just set output to stdout */
382 if (!doall)
383 return(stdout);
385 /* set output to a temporary file, so if no output don't send mail */
386 snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);
387 if ((fd = mkstemp(path)) < 0)
388 return(NULL);
389 return(fdopen(fd, "w+"));
392 static void
393 closecal(FILE *fp)
395 uid_t uid;
396 struct stat sbuf;
397 int nread, pdes[2], status;
398 char buf[1024];
400 if (!doall)
401 return;
403 rewind(fp);
404 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
405 goto done;
406 if (pipe(pdes) < 0)
407 goto done;
408 switch (fork()) {
409 case -1: /* error */
410 close(pdes[0]);
411 close(pdes[1]);
412 goto done;
413 case 0:
414 /* child -- set stdin to pipe output */
415 if (pdes[0] != STDIN_FILENO) {
416 dup2(pdes[0], STDIN_FILENO);
417 close(pdes[0]);
419 close(pdes[1]);
420 uid = geteuid();
421 if (setuid(getuid()) < 0) {
422 warnx("setuid failed");
423 _exit(EXIT_FAILURE);
425 if (setgid(getegid()) < 0) {
426 warnx("setgid failed");
427 _exit(EXIT_FAILURE);
429 if (setuid(uid) < 0) {
430 warnx("setuid failed");
431 _exit(EXIT_FAILURE);
433 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
434 "\"Reminder Service\"", NULL);
435 warn(_PATH_SENDMAIL);
436 _exit(EXIT_FAILURE);
438 /* parent -- write to pipe input */
439 close(pdes[0]);
441 header[1].iov_base = header[3].iov_base = pw->pw_name;
442 header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
443 writev(pdes[1], header, 7);
444 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
445 write(pdes[1], buf, nread);
446 close(pdes[1]);
447 done: fclose(fp);
448 unlink(path);
449 while (wait(&status) >= 0);