[BACK]Return to calendar.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / calendar

Diff for /src/usr.bin/calendar/calendar.c between version 1.1.1.1 and 1.17

version 1.1.1.1, 1995/10/18 08:44:57 version 1.17, 2002/02/16 21:27:44
Line 1 
Line 1 
 /*      $NetBSD: calendar.c,v 1.8 1995/09/02 05:38:38 jtc Exp $ */  /*      $OpenBSD$       */
   
 /*  /*
  * Copyright (c) 1989, 1993, 1994   * Copyright (c) 1989, 1993, 1994
Line 34 
Line 34 
  */   */
   
 #ifndef lint  #ifndef lint
 static char copyright[] =  static const char copyright[] =
 "@(#) Copyright (c) 1989, 1993\n\  "@(#) Copyright (c) 1989, 1993\n\
         The Regents of the University of California.  All rights reserved.\n";          The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */  #endif /* not lint */
   
 #ifndef lint  #ifndef lint
 #if 0  #if 0
 static char sccsid[] = "@(#)calendar.c  8.4 (Berkeley) 1/7/95";  static const char sccsid[] = "@(#)calendar.c  8.3 (Berkeley) 3/25/94";
   #else
   static char rcsid[] = "$OpenBSD$";
 #endif  #endif
 static char rcsid[] = "$NetBSD: calendar.c,v 1.8 1995/09/02 05:38:38 jtc Exp $";  
 #endif /* not lint */  #endif /* not lint */
   
 #include <sys/param.h>  
 #include <sys/time.h>  
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <sys/uio.h>  #include <sys/types.h>
 #include <sys/wait.h>  #include <sys/wait.h>
   
 #include <ctype.h>  
 #include <err.h>  #include <err.h>
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  #include <locale.h>
   #include <login_cap.h>
 #include <pwd.h>  #include <pwd.h>
   #include <signal.h>
 #include <stdio.h>  #include <stdio.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
   #include <time.h>
 #include <tzfile.h>  #include <tzfile.h>
 #include <unistd.h>  #include <unistd.h>
   
 #include "pathnames.h"  #include "pathnames.h"
   #include "calendar.h"
   
   char *calendarFile = "calendar";  /* default calendar file */
   char *calendarHome = ".calendar"; /* HOME */
   char *calendarNoMail = "nomail";  /* don't sent mail if this file exists */
   
 struct passwd *pw;  struct passwd *pw;
 int doall;  int doall = 0;
   time_t f_time = 0;
   int bodun_always = 0;
   
 void     cal __P((void));  int f_dayAfter = 0; /* days after current date */
 void     closecal __P((FILE *));  int f_dayBefore = 0; /* days before current date */
 int      getday __P((char *));  
 int      getfield __P((char *, char **, int *));  
 int      getmonth __P((char *));  
 int      isnow __P((char *));  
 FILE    *opencal __P((void));  
 void     settime __P((void));  
 void     usage __P((void));  
   
   struct specialev spev[NUMEV];
   
   void childsig(int);
   
 int  int
 main(argc, argv)  main(argc, argv)
         int argc;          int argc;
         char *argv[];          char *argv[];
 {  {
         extern int optind;  
         int ch;          int ch;
         char *caldir;          char *caldir;
   
         while ((ch = getopt(argc, argv, "-a")) != EOF)          (void)setlocale(LC_ALL, "");
   
           while ((ch = getopt(argc, argv, "-abf:t:A:B:")) != -1)
                 switch (ch) {                  switch (ch) {
                 case '-':               /* backward contemptible */                  case '-':               /* backward contemptible */
                 case 'a':                  case 'a':
                         if (getuid()) {                          if (getuid())
                                 errno = EPERM;                                  errx(1, "%s", strerror(EPERM));
                                 err(1, NULL);  
                         }  
                         doall = 1;                          doall = 1;
                         break;                          break;
                 case '?':  
                   case 'b':
                           bodun_always++;
                           break;
   
                   case 'f': /* other calendar file */
                           calendarFile = optarg;
                           break;
   
                   case 't': /* other date, undocumented, for tests */
                           if ((f_time = Mktime(optarg)) <= 0)
                                   errx(1, "specified date is outside allowed range");
                           break;
   
                   case 'A': /* days after current date */
                           f_dayAfter = atoi(optarg);
                           break;
   
                   case 'B': /* days before current date */
                           f_dayBefore = atoi(optarg);
                           break;
   
                 default:                  default:
                         usage();                          usage();
                 }                  }
Line 107 
Line 131 
         if (argc)          if (argc)
                 usage();                  usage();
   
         settime();          /* use current time */
         if (doall)          if (f_time <= 0)
                 while ((pw = getpwent()) != NULL) {              (void)time(&f_time);
                         (void)setegid(pw->pw_gid);  
                         (void)seteuid(pw->pw_uid);  
                         if (!chdir(pw->pw_dir))  
                                 cal();  
                         (void)seteuid(0);  
                 }  
         else if ((caldir = getenv("CALENDAR_DIR")) != NULL) {  
                         if(!chdir(caldir))  
                                 cal();  
         } else  
                 cal();  
         exit(0);  
 }  
   
 void          if (f_dayBefore) {
 cal()                  /* Move back in time and only look forwards */
 {                  f_dayAfter += f_dayBefore;
         register int printing;                  f_time -= SECSPERDAY * f_dayBefore;
         register char *p;                  f_dayBefore = 0;
         FILE *fp;  
         int ch;  
         char buf[2048 + 1];  
   
         if ((fp = opencal()) == NULL)  
                 return;  
         for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {  
                 if ((p = strchr(buf, '\n')) != NULL)  
                         *p = '\0';  
                 else  
                         while ((ch = getchar()) != '\n' && ch != EOF);  
                 if (buf[0] == '\0')  
                         continue;  
                 if (buf[0] != '\t')  
                         printing = isnow(buf) ? 1 : 0;  
                 if (printing)  
                         (void)fprintf(fp, "%s\n", buf);  
         }          }
         closecal(fp);          settime(&f_time);
 }  
   
 struct iovec header[] = {          if (doall) {
         "From: ", 6,                  pid_t kid, deadkid;
         NULL, 0,                  int kidstat, kidreaped, runningkids;
         " (Reminder Service)\nTo: ", 24,                  int acstat;
         NULL, 0,                  struct stat sbuf;
         "\nSubject: ", 10,                  time_t t;
         NULL, 0,                  unsigned int sleeptime;
         "'s Calendar\nPrecedence: bulk\n\n",  30,  
 };  
   
 /* 1-based month, 0-based days, cumulative */                  signal(SIGCHLD, childsig);
 int daytab[][14] = {                  runningkids = 0;
         0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364,                  t = time(NULL);
         0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365,                  while ((pw = getpwent()) != NULL) {
 };                          acstat = 0;
 struct tm *tp;                          /* Avoid unnecessary forks.  The calendar file is only
 int *cumdays, offset, yrdays;                           * opened as the user later; if it can't be opened,
 char dayname[10];                           * it's no big deal.  Also, get to correct directory.
                            * Note that in an NFS environment root may get EACCES
                            * on a chdir(), in which case we have to fork.  As long as
                            * we can chdir() we can stat(), unless the user is
                            * modifying permissions while this is running.
                            */
                           if (chdir(pw->pw_dir)) {
                                   if (errno == EACCES)
                                           acstat = 1;
                                   else
                                           continue;
                           }
                           if (stat(calendarFile, &sbuf) != 0) {
                                   if (chdir(calendarHome)) {
                                           if (errno == EACCES)
                                                   acstat = 1;
                                           else
                                                   continue;
                                   }
                                   if (stat(calendarNoMail, &sbuf) == 0 ||
                                       stat(calendarFile, &sbuf) != 0)
                                           continue;
                           }
                           sleeptime = USERTIMEOUT;
                           switch ((kid = fork())) {
                           case -1:        /* error */
                                   warn("fork");
                                   continue;
                           case 0: /* child */
                                   (void)setlocale(LC_ALL, "");
                                   if (setusercontext(NULL, pw, pw->pw_uid,
                                       LOGIN_SETALL ^ LOGIN_SETLOGIN))
                                           err(1, "unable to set user context (uid %d)",
                                               (int)pw->pw_uid);
                                   if (acstat) {
                                           if (chdir(pw->pw_dir) ||
                                               stat(calendarFile, &sbuf) != 0 ||
                                               chdir(calendarHome) ||
                                               stat(calendarNoMail, &sbuf) == 0 ||
                                               stat(calendarFile, &sbuf) != 0)
                                                   exit(0);
                                   }
                                   cal();
                                   exit(0);
                           }
                           /* parent: wait a reasonable time, then kill child if
                            * necessary.
                            */
                           runningkids++;
                           kidreaped = 0;
                           do {
                                   sleeptime = sleep(sleeptime);
                                   /* Note that there is the possibility, if the sleep
                                    * stops early due to some other signal, of the child
                                    * terminating and not getting detected during the next
                                    * sleep.  In that unlikely worst case, we just sleep
                                    * too long for that user.
                                    */
                                   for (;;) {
                                           deadkid = waitpid(-1, &kidstat, WNOHANG);
                                           if (deadkid <= 0)
                                                   break;
                                           runningkids--;
                                           if (deadkid == kid) {
                                                   kidreaped = 1;
                                                   sleeptime = 0;
                                           }
                                   }
                           } while (sleeptime);
   
 void                          if (!kidreaped) {
 settime()                                  /* It doesn't _really_ matter if the kill fails, e.g.
 {                                   * if there's only a zombie now.
         time_t now;                                   */
                                   (void)kill(kid, SIGTERM);
         (void)time(&now);                                  warnx("uid %d did not finish in time", (int)pw->pw_uid);
         tp = localtime(&now);                          }
         if (isleap(tp->tm_year + 1900)) {                          if (time(NULL) - t >= SECSPERDAY)
                 yrdays = DAYSPERLYEAR;                                  errx(2, "'calendar -a' took more than a day; stopped at uid %d",
                 cumdays = daytab[1];                                      (int)pw->pw_uid);
         } else {  
                 yrdays = DAYSPERNYEAR;  
                 cumdays = daytab[0];  
         }  
         /* Friday displays Monday's events */  
         offset = tp->tm_wday == 5 ? 3 : 1;  
         header[5].iov_base = dayname;  
         header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);  
 }  
   
 /*  
  * Possible date formats include any combination of:  
  *      3-charmonth                     (January, Jan, Jan)  
  *      3-charweekday                   (Friday, Monday, mon.)  
  *      numeric month or day            (1, 2, 04)  
  *  
  * Any character may separate them, or they may not be separated.  Any line,  
  * following a line that is matched, that starts with "whitespace", is shown  
  * along with the matched line.  
  */  
 int  
 isnow(endp)  
         char *endp;  
 {  
         int day, flags, month, v1, v2;  
   
 #define F_ISMONTH       0x01  
 #define F_ISDAY         0x02  
         flags = 0;  
         /* didn't recognize anything, skip it */  
         if (!(v1 = getfield(endp, &endp, &flags)))  
                 return (0);  
         if (flags & F_ISDAY || v1 > 12) {  
                 /* found a day */  
                 day = v1;  
                 month = tp->tm_mon + 1;  
         } else if (flags & F_ISMONTH) {  
                 month = v1;  
                 /* if no recognizable day, assume the first */  
                 if (!(day = getfield(endp, &endp, &flags)))  
                         day = 1;  
         } else {  
                 v2 = getfield(endp, &endp, &flags);  
                 if (flags & F_ISMONTH) {  
                         day = v1;  
                         month = v2;  
                 } else {  
                         /* F_ISDAY set, v2 > 12, or no way to tell */  
                         month = v1;  
                         /* if no recognizable day, assume the first */  
                         day = v2 ? v2 : 1;  
                 }                  }
         }                  for (;;) {
         if (flags & F_ISDAY)                          deadkid = waitpid(-1, &kidstat, WNOHANG);
                 day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);                          if (deadkid <= 0)
         day = cumdays[month] + day;                                  break;
                           runningkids--;
         /* if today or today + offset days */  
         if (day >= tp->tm_yday && day <= tp->tm_yday + offset)  
                 return (1);  
         /* if number of days left in this year + days to event in next year */  
         if (yrdays - tp->tm_yday + day <= offset)  
                 return (1);  
         return (0);  
 }  
   
 int  
 getfield(p, endp, flags)  
         char *p, **endp;  
         int *flags;  
 {  
         int val;  
         char *start, savech;  
   
         for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);  
         if (*p == '*') {                        /* `*' is current month */  
                 *flags |= F_ISMONTH;  
                 *endp = p+1;  
                 return (tp->tm_mon + 1);  
         }  
         if (isdigit(*p)) {  
                 val = strtol(p, &p, 10);        /* if 0, it's failure */  
                 for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);  
                 *endp = p;  
                 return (val);  
         }  
         for (start = p; isalpha(*++p););  
         savech = *p;  
         *p = '\0';  
         if ((val = getmonth(start)) != 0)  
                 *flags |= F_ISMONTH;  
         else if ((val = getday(start)) != 0)  
                 *flags |= F_ISDAY;  
         else {  
                 *p = savech;  
                 return (0);  
         }  
         for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);  
         *endp = p;  
         return (val);  
 }  
   
 char path[MAXPATHLEN + 1];  
   
 FILE *  
 opencal()  
 {  
         int fd, pdes[2];  
   
         /* open up calendar file as stdin */  
         if (!freopen("calendar", "r", stdin)) {  
                 if (doall)  
                         return (NULL);  
                 errx(1, "no calendar file.");  
         }  
         if (pipe(pdes) < 0)  
                 return (NULL);  
         switch (vfork()) {  
         case -1:                        /* error */  
                 (void)close(pdes[0]);  
                 (void)close(pdes[1]);  
                 return (NULL);  
         case 0:  
                 /* child -- stdin already setup, set stdout to pipe input */  
                 if (pdes[1] != STDOUT_FILENO) {  
                         (void)dup2(pdes[1], STDOUT_FILENO);  
                         (void)close(pdes[1]);  
                 }                  }
                 (void)close(pdes[0]);                  if (runningkids)
                 execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL);                          warnx(
                 warn("execl: %s", _PATH_CPP);  "%d child processes still running when 'calendar -a' finished", runningkids);
                 _exit(1);  
         }          }
         /* parent -- set stdin to pipe output */          else if ((caldir = getenv("CALENDAR_DIR")) != NULL) {
         (void)dup2(pdes[0], STDIN_FILENO);                  if(!chdir(caldir))
         (void)close(pdes[0]);                          cal();
         (void)close(pdes[1]);          } else
                   cal();
   
         /* not reading all calendar files, just set output to stdout */          exit(0);
         if (!doall)  
                 return (stdout);  
   
         /* set output to a temporary file, so if no output don't send mail */  
         (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);  
         if ((fd = mkstemp(path)) < 0)  
                 return (NULL);  
         return (fdopen(fd, "w+"));  
 }  }
   
   
 void  void
 closecal(fp)  usage()
         FILE *fp;  
 {  {
         struct stat sbuf;          (void)fprintf(stderr,
         int nread, pdes[2], status;              "usage: calendar [-a] [-A num] [-b] [-B num] [-t [[[cc]yy][mm]]dd] "
         char buf[1024];              "[-f calendarfile]\n");
           exit(1);
         if (!doall)  
                 return;  
   
         (void)rewind(fp);  
         if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)  
                 goto done;  
         if (pipe(pdes) < 0)  
                 goto done;  
         switch (vfork()) {  
         case -1:                        /* error */  
                 (void)close(pdes[0]);  
                 (void)close(pdes[1]);  
                 goto done;  
         case 0:  
                 /* child -- set stdin to pipe output */  
                 if (pdes[0] != STDIN_FILENO) {  
                         (void)dup2(pdes[0], STDIN_FILENO);  
                         (void)close(pdes[0]);  
                 }  
                 (void)close(pdes[1]);  
                 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",  
                     "\"Reminder Service\"", "-f", "root", NULL);  
                 warn("execl: %s", _PATH_SENDMAIL);  
                 _exit(1);  
         }  
         /* parent -- write to pipe input */  
         (void)close(pdes[0]);  
   
         header[1].iov_base = header[3].iov_base = pw->pw_name;  
         header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);  
         writev(pdes[1], header, 7);  
         while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)  
                 (void)write(pdes[1], buf, nread);  
         (void)close(pdes[1]);  
 done:   (void)fclose(fp);  
         (void)unlink(path);  
         while (wait(&status) >= 0);  
 }  }
   
 static char *months[] = {  
         "jan", "feb", "mar", "apr", "may", "jun",  
         "jul", "aug", "sep", "oct", "nov", "dec", NULL,  
 };  
   
 int  
 getmonth(s)  
         register char *s;  
 {  
         register char **p;  
   
         for (p = months; *p; ++p)  
                 if (!strncasecmp(s, *p, 3))  
                         return ((p - months) + 1);  
         return (0);  
 }  
   
 static char *days[] = {  
         "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,  
 };  
   
 int  
 getday(s)  
         register char *s;  
 {  
         register char **p;  
   
         for (p = days; *p; ++p)  
                 if (!strncasecmp(s, *p, 3))  
                         return ((p - days) + 1);  
         return (0);  
 }  
   
 void  void
 usage()  childsig(sig)
           int sig;
 {  {
         (void)fprintf(stderr, "usage: calendar [-a]\n");  
         exit(1);  
 }  }

Legend:
Removed from v.1.1.1.1  
changed lines
  Added in v.1.17