calendar.c revision 11334
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1989, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 351590Srgrimesstatic char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1989, 1993\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 381590Srgrimes#endif /* not lint */ 391590Srgrimes 401590Srgrimes#ifndef lint 411590Srgrimesstatic char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; 421590Srgrimes#endif /* not lint */ 431590Srgrimes 441590Srgrimes#include <sys/param.h> 451590Srgrimes#include <sys/time.h> 461590Srgrimes#include <sys/stat.h> 471590Srgrimes#include <sys/uio.h> 481590Srgrimes#include <sys/wait.h> 491590Srgrimes 501590Srgrimes#include <ctype.h> 511590Srgrimes#include <err.h> 521590Srgrimes#include <errno.h> 531590Srgrimes#include <fcntl.h> 541590Srgrimes#include <pwd.h> 551590Srgrimes#include <stdio.h> 561590Srgrimes#include <stdlib.h> 571590Srgrimes#include <string.h> 581590Srgrimes#include <unistd.h> 591590Srgrimes 601590Srgrimes#include "pathnames.h" 611590Srgrimes 621590Srgrimesstruct passwd *pw; 631590Srgrimesint doall; 641590Srgrimes 651590Srgrimesvoid cal __P((void)); 661590Srgrimesvoid closecal __P((FILE *)); 671590Srgrimesint getday __P((char *)); 681590Srgrimesint getfield __P((char *, char **, int *)); 691590Srgrimesint getmonth __P((char *)); 701590Srgrimesint isnow __P((char *)); 711590SrgrimesFILE *opencal __P((void)); 721590Srgrimesvoid settime __P((void)); 731590Srgrimesvoid usage __P((void)); 749987Swollman#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0) 751590Srgrimes 761590Srgrimesint 771590Srgrimesmain(argc, argv) 781590Srgrimes int argc; 791590Srgrimes char *argv[]; 801590Srgrimes{ 811590Srgrimes extern int optind; 821590Srgrimes int ch; 831590Srgrimes 841590Srgrimes while ((ch = getopt(argc, argv, "-a")) != EOF) 851590Srgrimes switch (ch) { 861590Srgrimes case '-': /* backward contemptible */ 871590Srgrimes case 'a': 881590Srgrimes if (getuid()) { 891590Srgrimes errno = EPERM; 901590Srgrimes err(1, NULL); 911590Srgrimes } 921590Srgrimes doall = 1; 931590Srgrimes break; 941590Srgrimes case '?': 951590Srgrimes default: 961590Srgrimes usage(); 971590Srgrimes } 981590Srgrimes argc -= optind; 991590Srgrimes argv += optind; 1001590Srgrimes 1011590Srgrimes if (argc) 1021590Srgrimes usage(); 1031590Srgrimes 1041590Srgrimes settime(); 1051590Srgrimes if (doall) 1061590Srgrimes while ((pw = getpwent()) != NULL) { 1071590Srgrimes (void)setegid(pw->pw_gid); 1081590Srgrimes (void)seteuid(pw->pw_uid); 1091590Srgrimes if (!chdir(pw->pw_dir)) 1101590Srgrimes cal(); 1111590Srgrimes (void)seteuid(0); 1121590Srgrimes } 11311334Sache else 1141590Srgrimes cal(); 1151590Srgrimes exit(0); 1161590Srgrimes} 1171590Srgrimes 1181590Srgrimesvoid 1191590Srgrimescal() 1201590Srgrimes{ 1211590Srgrimes register int printing; 1221590Srgrimes register char *p; 1231590Srgrimes FILE *fp; 1241590Srgrimes int ch; 1251590Srgrimes char buf[2048 + 1]; 1261590Srgrimes 1271590Srgrimes if ((fp = opencal()) == NULL) 1281590Srgrimes return; 1291590Srgrimes for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) { 1301590Srgrimes if ((p = strchr(buf, '\n')) != NULL) 1311590Srgrimes *p = '\0'; 1321590Srgrimes else 1331590Srgrimes while ((ch = getchar()) != '\n' && ch != EOF); 1341590Srgrimes if (buf[0] == '\0') 1351590Srgrimes continue; 1361590Srgrimes if (buf[0] != '\t') 1371590Srgrimes printing = isnow(buf) ? 1 : 0; 1381590Srgrimes if (printing) 1391590Srgrimes (void)fprintf(fp, "%s\n", buf); 1401590Srgrimes } 1411590Srgrimes closecal(fp); 1421590Srgrimes} 1431590Srgrimes 1441590Srgrimesstruct iovec header[] = { 1451590Srgrimes "From: ", 6, 1461590Srgrimes NULL, 0, 1471590Srgrimes " (Reminder Service)\nTo: ", 24, 1481590Srgrimes NULL, 0, 1491590Srgrimes "\nSubject: ", 10, 1501590Srgrimes NULL, 0, 1511590Srgrimes "'s Calendar\nPrecedence: bulk\n\n", 30, 1521590Srgrimes}; 1531590Srgrimes 1541590Srgrimes/* 1-based month, 0-based days, cumulative */ 1551590Srgrimesint daytab[][14] = { 1561590Srgrimes 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364, 1571590Srgrimes 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 1581590Srgrimes}; 1591590Srgrimesstruct tm *tp; 1601590Srgrimesint *cumdays, offset, yrdays; 1611590Srgrimeschar dayname[10]; 1621590Srgrimes 1631590Srgrimesvoid 1641590Srgrimessettime() 1651590Srgrimes{ 1661590Srgrimes time_t now; 1671590Srgrimes 1681590Srgrimes (void)time(&now); 1691590Srgrimes tp = localtime(&now); 1701590Srgrimes if (isleap(tp->tm_year + 1900)) { 1719987Swollman yrdays = 366; 1721590Srgrimes cumdays = daytab[1]; 1731590Srgrimes } else { 1749987Swollman yrdays = 365; 1751590Srgrimes cumdays = daytab[0]; 1761590Srgrimes } 1771590Srgrimes /* Friday displays Monday's events */ 1781590Srgrimes offset = tp->tm_wday == 5 ? 3 : 1; 1791590Srgrimes header[5].iov_base = dayname; 1801590Srgrimes header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp); 1811590Srgrimes} 1821590Srgrimes 1831590Srgrimes/* 1841590Srgrimes * Possible date formats include any combination of: 1851590Srgrimes * 3-charmonth (January, Jan, Jan) 1861590Srgrimes * 3-charweekday (Friday, Monday, mon.) 1871590Srgrimes * numeric month or day (1, 2, 04) 1881590Srgrimes * 1891590Srgrimes * Any character may separate them, or they may not be separated. Any line, 1901590Srgrimes * following a line that is matched, that starts with "whitespace", is shown 1911590Srgrimes * along with the matched line. 1921590Srgrimes */ 1931590Srgrimesint 1941590Srgrimesisnow(endp) 1951590Srgrimes char *endp; 1961590Srgrimes{ 1971590Srgrimes int day, flags, month, v1, v2; 1981590Srgrimes 1991590Srgrimes#define F_ISMONTH 0x01 2001590Srgrimes#define F_ISDAY 0x02 2011590Srgrimes flags = 0; 2021590Srgrimes /* didn't recognize anything, skip it */ 2031590Srgrimes if (!(v1 = getfield(endp, &endp, &flags))) 2041590Srgrimes return (0); 2051590Srgrimes if (flags & F_ISDAY || v1 > 12) { 2061590Srgrimes /* found a day */ 2071590Srgrimes day = v1; 2081590Srgrimes /* if no recognizable month, assume just a day alone */ 2091590Srgrimes if (!(month = getfield(endp, &endp, &flags))) 2101590Srgrimes month = tp->tm_mon + 1; 2111590Srgrimes } else if (flags & F_ISMONTH) { 2121590Srgrimes month = v1; 2131590Srgrimes /* if no recognizable day, assume the first */ 2141590Srgrimes if (!(day = getfield(endp, &endp, &flags))) 2151590Srgrimes day = 1; 2161590Srgrimes } else { 2171590Srgrimes v2 = getfield(endp, &endp, &flags); 2181590Srgrimes if (flags & F_ISMONTH) { 2191590Srgrimes day = v1; 2201590Srgrimes month = v2; 2211590Srgrimes } else { 2221590Srgrimes /* F_ISDAY set, v2 > 12, or no way to tell */ 2231590Srgrimes month = v1; 2241590Srgrimes /* if no recognizable day, assume the first */ 2251590Srgrimes day = v2 ? v2 : 1; 2261590Srgrimes } 2271590Srgrimes } 2281590Srgrimes if (flags & F_ISDAY) 2291590Srgrimes day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); 2301590Srgrimes day = cumdays[month] + day; 2311590Srgrimes 2321590Srgrimes /* if today or today + offset days */ 2331590Srgrimes if (day >= tp->tm_yday && day <= tp->tm_yday + offset) 2341590Srgrimes return (1); 2351590Srgrimes /* if number of days left in this year + days to event in next year */ 2361590Srgrimes if (yrdays - tp->tm_yday + day <= offset) 2371590Srgrimes return (1); 2381590Srgrimes return (0); 2391590Srgrimes} 2401590Srgrimes 2411590Srgrimesint 2421590Srgrimesgetfield(p, endp, flags) 2431590Srgrimes char *p, **endp; 2441590Srgrimes int *flags; 2451590Srgrimes{ 2461590Srgrimes int val; 2471590Srgrimes char *start, savech; 2481590Srgrimes 2491590Srgrimes for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); 2501590Srgrimes if (*p == '*') { /* `*' is current month */ 2511590Srgrimes *flags |= F_ISMONTH; 2521590Srgrimes *endp = p+1; 2531590Srgrimes return (tp->tm_mon + 1); 2541590Srgrimes } 2551590Srgrimes if (isdigit(*p)) { 2561590Srgrimes val = strtol(p, &p, 10); /* if 0, it's failure */ 2571590Srgrimes for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); 2581590Srgrimes *endp = p; 2591590Srgrimes return (val); 2601590Srgrimes } 2611590Srgrimes for (start = p; isalpha(*++p);); 2621590Srgrimes savech = *p; 2631590Srgrimes *p = '\0'; 2641590Srgrimes if ((val = getmonth(start)) != 0) 2651590Srgrimes *flags |= F_ISMONTH; 2661590Srgrimes else if ((val = getday(start)) != 0) 2671590Srgrimes *flags |= F_ISDAY; 2681590Srgrimes else { 2691590Srgrimes *p = savech; 2701590Srgrimes return (0); 2711590Srgrimes } 2721590Srgrimes for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); 2731590Srgrimes *endp = p; 2741590Srgrimes return (val); 2751590Srgrimes} 2761590Srgrimes 2771590Srgrimeschar path[MAXPATHLEN + 1]; 2781590Srgrimes 2791590SrgrimesFILE * 2801590Srgrimesopencal() 2811590Srgrimes{ 2821590Srgrimes int fd, pdes[2]; 2831590Srgrimes 2841590Srgrimes /* open up calendar file as stdin */ 2851590Srgrimes if (!freopen("calendar", "r", stdin)) { 2861590Srgrimes if (doall) 2871590Srgrimes return (NULL); 2881590Srgrimes errx(1, "no calendar file."); 2891590Srgrimes } 2908874Srgrimes if (pipe(pdes) < 0) 2911590Srgrimes return (NULL); 2921590Srgrimes switch (vfork()) { 2931590Srgrimes case -1: /* error */ 2941590Srgrimes (void)close(pdes[0]); 2951590Srgrimes (void)close(pdes[1]); 2961590Srgrimes return (NULL); 2971590Srgrimes case 0: 2981590Srgrimes /* child -- stdin already setup, set stdout to pipe input */ 2991590Srgrimes if (pdes[1] != STDOUT_FILENO) { 3001590Srgrimes (void)dup2(pdes[1], STDOUT_FILENO); 3011590Srgrimes (void)close(pdes[1]); 3021590Srgrimes } 3031590Srgrimes (void)close(pdes[0]); 3045311Sache execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL); 3051590Srgrimes (void)fprintf(stderr, 3061590Srgrimes "calendar: execl: %s: %s.\n", _PATH_CPP, strerror(errno)); 3071590Srgrimes _exit(1); 3081590Srgrimes } 3091590Srgrimes /* parent -- set stdin to pipe output */ 3101590Srgrimes (void)dup2(pdes[0], STDIN_FILENO); 3111590Srgrimes (void)close(pdes[0]); 3121590Srgrimes (void)close(pdes[1]); 3131590Srgrimes 3141590Srgrimes /* not reading all calendar files, just set output to stdout */ 3151590Srgrimes if (!doall) 3161590Srgrimes return (stdout); 3171590Srgrimes 3181590Srgrimes /* set output to a temporary file, so if no output don't send mail */ 3191590Srgrimes (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 3201590Srgrimes if ((fd = mkstemp(path)) < 0) 3211590Srgrimes return (NULL); 3221590Srgrimes return (fdopen(fd, "w+")); 3231590Srgrimes} 3241590Srgrimes 3251590Srgrimesvoid 3261590Srgrimesclosecal(fp) 3271590Srgrimes FILE *fp; 3281590Srgrimes{ 3291590Srgrimes struct stat sbuf; 3301590Srgrimes int nread, pdes[2], status; 3311590Srgrimes char buf[1024]; 3321590Srgrimes 3331590Srgrimes if (!doall) 3341590Srgrimes return; 3351590Srgrimes 3361590Srgrimes (void)rewind(fp); 3371590Srgrimes if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 3381590Srgrimes goto done; 3398874Srgrimes if (pipe(pdes) < 0) 3401590Srgrimes goto done; 3411590Srgrimes switch (vfork()) { 3421590Srgrimes case -1: /* error */ 3431590Srgrimes (void)close(pdes[0]); 3441590Srgrimes (void)close(pdes[1]); 3451590Srgrimes goto done; 3468874Srgrimes case 0: 3471590Srgrimes /* child -- set stdin to pipe output */ 3481590Srgrimes if (pdes[0] != STDIN_FILENO) { 3491590Srgrimes (void)dup2(pdes[0], STDIN_FILENO); 3501590Srgrimes (void)close(pdes[0]); 3511590Srgrimes } 3521590Srgrimes (void)close(pdes[1]); 3531590Srgrimes execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 3541590Srgrimes "\"Reminder Service\"", "-f", "root", NULL); 3551590Srgrimes (void)fprintf(stderr, 3561590Srgrimes "calendar: %s: %s.\n", _PATH_SENDMAIL, strerror(errno)); 3571590Srgrimes _exit(1); 3581590Srgrimes } 3591590Srgrimes /* parent -- write to pipe input */ 3601590Srgrimes (void)close(pdes[0]); 3611590Srgrimes 3621590Srgrimes header[1].iov_base = header[3].iov_base = pw->pw_name; 3631590Srgrimes header[1].iov_len = header[3].iov_len = strlen(pw->pw_name); 3641590Srgrimes writev(pdes[1], header, 7); 3651590Srgrimes while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 3661590Srgrimes (void)write(pdes[1], buf, nread); 3671590Srgrimes (void)close(pdes[1]); 3681590Srgrimesdone: (void)fclose(fp); 3691590Srgrimes (void)unlink(path); 3701590Srgrimes while (wait(&status) >= 0); 3711590Srgrimes} 3721590Srgrimes 3731590Srgrimesstatic char *months[] = { 3741590Srgrimes "jan", "feb", "mar", "apr", "may", "jun", 3751590Srgrimes "jul", "aug", "sep", "oct", "nov", "dec", NULL, 3761590Srgrimes}; 3771590Srgrimes 3781590Srgrimesint 3791590Srgrimesgetmonth(s) 3801590Srgrimes register char *s; 3811590Srgrimes{ 3821590Srgrimes register char **p; 3831590Srgrimes 3841590Srgrimes for (p = months; *p; ++p) 3851590Srgrimes if (!strncasecmp(s, *p, 3)) 3861590Srgrimes return ((p - months) + 1); 3871590Srgrimes return (0); 3881590Srgrimes} 3891590Srgrimes 3901590Srgrimesstatic char *days[] = { 3911590Srgrimes "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL, 3921590Srgrimes}; 3931590Srgrimes 3941590Srgrimesint 3951590Srgrimesgetday(s) 3961590Srgrimes register char *s; 3971590Srgrimes{ 3981590Srgrimes register char **p; 3991590Srgrimes 4001590Srgrimes for (p = days; *p; ++p) 4011590Srgrimes if (!strncasecmp(s, *p, 3)) 4021590Srgrimes return ((p - days) + 1); 4031590Srgrimes return (0); 4041590Srgrimes} 4051590Srgrimes 4061590Srgrimesvoid 4071590Srgrimesusage() 4081590Srgrimes{ 4091590Srgrimes (void)fprintf(stderr, "usage: calendar [-a]\n"); 4101590Srgrimes exit(1); 4111590Srgrimes} 412