1: /*
   2:  * Copyright (c) 1983 Regents of the University of California.
   3:  * All rights reserved.  The Berkeley software License Agreement
   4:  * specifies the terms and conditions for redistribution.
   5:  */
   6: 
   7: #ifndef lint
   8: char copyright[] =
   9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  10:  All rights reserved.\n";
  11: #endif not lint
  12: 
  13: #ifndef lint
  14: static char sccsid[] = "@(#)at.c	5.4 (Berkeley) 5/28/86";
  15: #endif not lint
  16: 
  17: /*
  18:  *	Synopsis:	at [-s] [-c] [-m] time [filename]
  19:  *
  20:  *
  21:  *
  22:  *	Execute commands at a later date.
  23:  *
  24:  *
  25:  *	Modifications by:	Steve Wall
  26:  *				Computer Systems Research Group
  27:  *				University of California @ Berkeley
  28:  *
  29:  */
  30: #include <stdio.h>
  31: #include <ctype.h>
  32: #include <signal.h>
  33: #include <pwd.h>
  34: #include <sys/param.h>
  35: #include <sys/time.h>
  36: #include <sys/file.h>
  37: 
  38: #define HOUR        100     /* 1 hour (using military time) */
  39: #define HALFDAY     (12 * HOUR) /* half a day (12 hours) */
  40: #define FULLDAY     (24 * HOUR) /* a full day (24 hours) */
  41: 
  42: #define WEEK        1       /* day requested is 'week' */
  43: #define DAY     2       /* day requested is a weekday */
  44: #define MONTH       3       /* day requested is a month */
  45: 
  46: #define BOURNE      "/bin/sh"   /* run commands with Bourne shell*/
  47: #define CSHELL      "/bin/csh"  /* run commands with C shell */
  48: 
  49: #define NODATEFOUND -1      /* no date was given on command line */
  50: 
  51: #define ATDIR       "/usr/spool/at"     /* spooling area */
  52: 
  53: #define LINSIZ      256     /* length of input buffer */
  54: 
  55: /*
  56:  * A table to identify potential command line values for "time".
  57:  *
  58:  * We need this so that we can do some decent error checking on the
  59:  * command line arguments. (This was inspired by the old "at", which
  60:  * accepted "at 900 jan 55" as valid input and other small bugs.
  61:  */
  62: struct datetypes {
  63:     int type;
  64:     char *name;
  65: } dates_info[22] = {
  66:     { DAY,   "sunday"    },
  67:     { DAY,   "monday"    },
  68:     { DAY,   "tuesday"   },
  69:     { DAY,   "wednesday" },
  70:     { DAY,   "thursday"  },
  71:     { DAY,   "friday"    },
  72:     { DAY,   "saturday"  },
  73:     { MONTH, "january"   },
  74:     { MONTH, "february"  },
  75:     { MONTH, "march"     },
  76:     { MONTH, "april"     },
  77:     { MONTH, "may"       },
  78:     { MONTH, "june"      },
  79:     { MONTH, "july"      },
  80:     { MONTH, "august"    },
  81:     { MONTH, "september" },
  82:     { MONTH, "october"   },
  83:     { MONTH, "november"  },
  84:     { MONTH, "december"  },
  85:     { 0, ""},
  86: };
  87: 
  88: /*
  89:  * Months of the year.
  90:  */
  91: char *months[13] = {
  92:     "jan", "feb", "mar", "apr", "may", "jun",
  93:     "jul", "aug", "sep", "oct", "nov", "dec", 0,
  94: };
  95: 
  96: /*
  97:  * A table of the number of days in each month of the year.
  98:  *
  99:  *	yeartable[0] -- normal year
 100:  *	yeartable[1] -- leap year
 101:  */
 102: static int yeartable[2][13] = {
 103:     { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
 104:     { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
 105: };
 106: 
 107: /*
 108:  * Structure holding the relevant values needed to create a spoolfile.
 109:  * "attime" will contain the info about when a job is to be run, and
 110:  * "nowtime" will contain info about what time the "at" command is in-
 111:  * voked.
 112:  */
 113: struct times {
 114:     int year;           /* year that job is to be run */
 115:     int yday;           /* day of year that job is to be run */
 116:     int mon;            /* month of year that job is to be run*/
 117:     int mday;           /* day of month that job is to be run */
 118:     int wday;           /* day of week that job is to be run */
 119:     int hour;           /* hour of day that job is to be run */
 120:     int min;            /* min. of hour that job is to be run */
 121: } attime, nowtime;
 122: 
 123: char    atfile[100];            /* name of spoolfile "yy.ddd.hhhh.??" */
 124: char    *getenv();          /* get info on user's environment */
 125: char    **environ;          /* user's environment */
 126: FILE    *spoolfile;         /* spool file */
 127: FILE    *inputfile;         /* input file ("stdin" or "filename") */
 128: char    *getwd();           /* used to get current directory info */
 129: 
 130: 
 131: main(argc, argv)
 132: int argc;
 133: char **argv;
 134: {
 135:     int c;              /* scratch variable */
 136:     int usage();            /* print usage info and exit */
 137:     int cleanup();          /* do cleanup on an interrupt signal */
 138:     int dateindex = NODATEFOUND;    /* if a day is specified, what option
 139: 					   is it? (mon day, week, dayofweek) */
 140:     char *shell = BOURNE;       /* what shell do we use to run job? */
 141:     int shflag = 0;         /* override the current shell and run
 142: 					   job using the Bourne Shell */
 143:     int cshflag = 0;        /* override the current shell and run
 144: 					   job using the Cshell */
 145:     int mailflag = 0;       /* send mail after a job has been run?*/
 146:     int standardin = 0;     /* are we reading from stardard input */
 147:     char *tmp;          /* scratch pointer */
 148:     char line[LINSIZ];      /* a line from input file */
 149:     char pwbuf[MAXPATHLEN];     /* the current working directory */
 150:     char *jobfile = "stdin";    /* file containing job to be run */
 151:     char *getname();        /* get the login name of a user */
 152:     int pid;            /* For forking for security reasons */
 153: 
 154: 
 155: 
 156:     argv++; argc--;
 157: 
 158:     /*
 159: 	 * Interpret command line flags if they exist.
 160: 	 */
 161:     while (argc > 0 && **argv == '-') {
 162:         (*argv)++;
 163:         while (**argv) switch (*(*argv)++) {
 164: 
 165:             case 'c' :  cshflag++;
 166:                     shell = CSHELL;
 167:                     break;
 168: 
 169:             case 's' :  shflag++;
 170:                     shell = BOURNE;
 171:                     break;
 172: 
 173:             case 'm' :  mailflag++;
 174:                     break;
 175: 
 176:             default  :  usage();
 177: 
 178:         }
 179:         --argc, ++argv;
 180:     }
 181:     if (shflag && cshflag) {
 182:         fprintf(stderr,"ambiguous shell request.\n");
 183:         exit(1);
 184:     }
 185: 
 186:     /*
 187: 	 * Get the time it is when "at" is invoked. We set both nowtime and
 188: 	 * attime to this value so that as we interpret the time the job is to
 189: 	 * be run we can compare the two values to determine such things as
 190: 	 * whether of not the job should be run the same day the "at" command
 191: 	 * is given, whether a job is to be run next year, etc.
 192: 	 */
 193:     getnowtime(&nowtime, &attime);
 194: 
 195: #ifdef DEBUG
 196:     printit();
 197: #endif
 198: 
 199:     if (argc <= 0)
 200:         usage();
 201: 
 202:     /*
 203: 	 * Interpret argv[1] and create the time of day that the job is to
 204: 	 * be run. This is the same function that was used in the old "at"
 205: 	 */
 206:     maketime(&attime, *argv);
 207:     --argc; ++argv;
 208: 
 209: #ifdef DEBUG
 210:     printf("\n\nAFTER MAKETIME\n");
 211:     printit();
 212: #endif
 213: 
 214:     /*
 215: 	 * If argv[(2)] exists, this is a request to run a job on a certain
 216: 	 * day of year or a certain day of week.
 217: 	 *
 218: 	 * We send  argv to the function "getdateindex" which returns the
 219: 	 * index value of the requested day in the table "dates_info"
 220: 	 * (see line 50 for table). If 'getdateindex" returns a NODATEFOUND,
 221: 	 * then the requested day format was not found in the table (usually
 222: 	 * this means that the argument is a "filename"). If the requested
 223: 	 * day is found, we continue to process command line arguments.
 224: 	 */
 225:     if (argc > 0) {
 226:         if ((dateindex = getdateindex(*argv)) != NODATEFOUND) {
 227: 
 228:             ++argv; --argc;
 229: 
 230:             /*
 231: 			 * Determine the day of year that the job will be run
 232: 			 * depending on the value of argv.
 233: 			 */
 234:             makedayofyear(dateindex, &argv, &argc);
 235:         }
 236:     }
 237: 
 238:     /*
 239: 	 * If we get to this point and "dateindex" is set to NODATEFOUND,
 240: 	 * then we are dealing with a request with only a "time" specified
 241: 	 * (i.e. at 400p) and perhaps 'week' specified (i.e. at 400p week).
 242: 	 * If 'week' is specified, we just set excecution for 7 days in the
 243: 	 * future. Otherwise, we need to check to see if the requested time
 244: 	 * has already passed for the current day. If it has, then we add
 245: 	 * one to the day of year that the job will be executed.
 246: 	 */
 247:     if (dateindex == NODATEFOUND) {
 248:         int daysinyear;
 249:         if ((argc > 0) && (strcmp(*argv,"week") == 0)) {
 250:             attime.yday += 7;
 251:             ++argv; --argc;
 252:         } else if (istomorrow())
 253:             ++attime.yday;
 254: 
 255:         daysinyear = isleap(attime.year) ? 366 : 365;
 256:         if (attime.yday >= daysinyear) {
 257:             attime.yday -= daysinyear;
 258:             ++attime.year;
 259:         }
 260:     }
 261: 
 262:     /*
 263: 	 * If no more arguments exist, then we are reading
 264: 	 * from standard input. Thus, we set the standard
 265: 	 * input flag (++standardin).
 266: 	 */
 267:     if (argc <= 0)
 268:         ++standardin;
 269: 
 270: 
 271: #ifdef DEBUG
 272:     printf("\n\nAFTER ADDDAYS\n");
 273:     printit();
 274: #endif
 275: 
 276:     /*
 277: 	 * Start off assuming we're going to read from standard input,
 278: 	 * but if a filename has been given to read from, we will open it
 279: 	 * later.
 280: 	 */
 281:     inputfile = stdin;
 282: 
 283:     /*
 284: 	 * Create the filename for the spoolfile.
 285: 	 */
 286:     makeatfile(atfile,attime.year,attime.yday,attime.hour,attime.min);
 287: 
 288:     /*
 289: 	 * Open the spoolfile for writing.
 290: 	 */
 291:     if ((spoolfile = fopen(atfile, "w")) == NULL){
 292:         perror(atfile);
 293:         exit(1);
 294:     }
 295: 
 296:     /*
 297: 	 * Make the file not world readable.
 298: 	 */
 299:     fchmod(fileno(spoolfile), 0400);
 300: 
 301:     /*
 302: 	 * The protection mechanism works like this:
 303: 	 * We are running ruid=user, euid=daemon.  So far we have been
 304: 	 * messing around in the spool directory, so we needed the
 305: 	 * daemon stuff.  Now, we want to read the users file,
 306: 	 * so we must give up the daemon protection,  but we might
 307: 	 * need the daemon's protection if the user interrupts and
 308: 	 * we need to remove the spool files.
 309: 	 * So, we fork and let the kid set the real and effective
 310: 	 * user id's to the user, so he can read everything of his
 311: 	 * own, but not his professor's final exam and not stuff
 312: 	 * owned by daemon.  If the kid exits with non-zero status,
 313: 	 * that means that the user typed interrupt, and the parent
 314: 	 * (still with daemon permissions) removes the spool file.
 315: 	 */
 316:     signal(SIGINT, SIG_IGN);
 317:     pid = fork();
 318:     if (pid == -1) {
 319:         perror("fork");
 320:         exit(1);
 321:     }
 322:     if (pid) {
 323:         int wpid, status;
 324: 
 325:         /*
 326: 		 * We are the parent. If the kid has problems,
 327: 		 * cleanup the spool directory.
 328: 		 */
 329:         wpid = wait(&status);
 330:         if (wpid != pid || status) {
 331:             cleanup();
 332:             exit(1);
 333:         }
 334:         /*
 335: 		 * The kid should have alread flushed the buffers.
 336: 		 */
 337:         _exit(0);
 338:     }
 339: 
 340:     /*
 341: 	 * Exit on interrupt.
 342: 	 */
 343:     signal(SIGINT, SIG_DFL);
 344: 
 345:     /*
 346: 	 * We are the kid, give up daemon permissions.
 347: 	 */
 348:     setuid(getuid());
 349: 
 350:     /*
 351: 	 * Open the input file with the user's permissions.
 352: 	 */
 353:     if (!standardin) {
 354:         jobfile = *argv;
 355:         if ((inputfile = fopen(jobfile, "r")) == NULL) {
 356:             perror(jobfile);
 357:             exit(1);
 358:         }
 359:     }
 360: 
 361:     /*
 362: 	 * If the inputfile is not from a tty then turn off standardin
 363: 	 * If the inputfile is a tty, put out a prompt now, instead of
 364: 	 * waiting for a lot of file activity to complete.
 365: 	 */
 366:     if (!(isatty(fileno(inputfile))))
 367:         standardin = 0 ;
 368:     if (standardin) {
 369:         fputs("at> ", stdout);
 370:         fflush(stdout);
 371:     }
 372: 
 373:     /*
 374: 	 * Determine what shell we should use to run the job. If the user
 375: 	 * didn't explicitly request that his/her current shell be over-
 376: 	 * ridden (shflag of cshflag) then we use the current shell.
 377: 	 */
 378:     if ((!shflag) && (!cshflag) && (getenv("SHELL") != NULL))
 379:         shell = "$SHELL";
 380: 
 381:     /*
 382: 	 * Put some standard information at the top of the spoolfile.
 383: 	 * This info is used by the other "at"-oriented programs (atq,
 384: 	 * atrm, atrun).
 385: 	 */
 386:     fprintf(spoolfile, "# owner: %.127s\n",getname(getuid()));
 387:     fprintf(spoolfile, "# jobname: %.127s\n",jobfile);
 388:     fprintf(spoolfile, "# shell: sh\n");
 389:     fprintf(spoolfile, "# notify by mail: %s\n",(mailflag) ? "yes" : "no");
 390:     fprintf(spoolfile, "\n");
 391: 
 392:     /*
 393: 	 * Set the modes for any files created by the job being run.
 394: 	 */
 395:     c = umask(0);
 396:     umask(c);
 397:     fprintf(spoolfile, "umask %.1o\n", c);
 398: 
 399:     /*
 400: 	 * Get the current working directory so we know what directory to
 401: 	 * run the job from.
 402: 	 */
 403:     if (getwd(pwbuf) == NULL) {
 404:         fprintf(stderr, "at: can't get working directory\n");
 405:         exit(1);
 406:     }
 407:     fprintf(spoolfile, "cd %s\n", pwbuf);
 408: 
 409:     /*
 410: 	 * Copy the user's environment to the spoolfile.
 411: 	 */
 412:     if (environ) {
 413:         copyenvironment(&spoolfile);
 414:     }
 415: 
 416:     /*
 417: 	 * Put in a line to run the proper shell using the rest of
 418: 	 * the file as input.  Note that 'exec'ing the shell will
 419: 	 * cause sh() to leave a /tmp/sh### file around.
 420: 	 */
 421:     fprintf(spoolfile,
 422:         "%s << '...the rest of this file is shell input'\n", shell);
 423: 
 424:     /*
 425: 	 * Now that we have all the files set up, we can start reading in
 426: 	 * the job. (I added the prompt "at>" so that the user could tell
 427: 	 * when/if he/she was supposed to enter commands from standard
 428: 	 * input. The old "at" just sat there and didn't send any kind of
 429: 	 * message that said it was waiting for input if it was reading
 430: 	 * form standard input).
 431: 	 */
 432:     while (fgets(line, LINSIZ, inputfile) != NULL) {
 433:         fputs(line, spoolfile);
 434:         if (standardin)
 435:             fputs("at> ", stdout);
 436:     }
 437:     if (standardin)
 438:         fputs("<EOT>\n", stdout);   /* clean up the final output */
 439: 
 440:     /*
 441: 	 * Close all files and change the mode of the spoolfile.
 442: 	 */
 443:     fclose(inputfile);
 444:     fclose(spoolfile);
 445: 
 446:     exit(0);
 447: 
 448: }
 449: 
 450: /*
 451:  * Copy the user's environment to the spoolfile in the syntax of the
 452:  * Bourne shell.  After the environment is set up, the proper shell
 453:  * will be invoked.
 454:  */
 455: copyenvironment(spoolfile)
 456: FILE **spoolfile;
 457: {
 458:     char *tmp;          /* scratch pointer */
 459:     char **environptr = environ;    /* pointer to an environment setting */
 460: 
 461:     while(*environptr) {
 462:         tmp = *environptr;
 463: 
 464:         /*
 465: 		 * We don't want the termcap or terminal entry so skip them.
 466: 		 */
 467:         if ((strncmp(tmp,"TERM=",5) == 0) ||
 468:             (strncmp(tmp,"TERMCAP=",8) == 0)) {
 469:             ++environptr;
 470:             continue;
 471:         }
 472: 
 473:         /*
 474: 		 * Set up the proper syntax.
 475: 		 */
 476:         while (*tmp != '=')
 477:             fputc(*tmp++,*spoolfile);
 478:         fputc('=', *spoolfile);
 479:         fputc('\'' , *spoolfile);
 480:         ++tmp;
 481: 
 482:         /*
 483: 		 * Now copy the entry.
 484: 		 */
 485:         while (*tmp) {
 486:             if (*tmp == '\'')
 487:                 fputs("'\\''", *spoolfile);
 488:             else if (*tmp == '\n')
 489:                 fputs("\\",*spoolfile);
 490:             else
 491:                 fputc(*tmp, *spoolfile);
 492:             ++tmp;
 493:         }
 494:         fputc('\'' , *spoolfile);
 495: 
 496:         /*
 497: 		 * We need to "export" environment settings.
 498: 		 */
 499:         fprintf(*spoolfile, "\nexport ");
 500:         tmp = *environptr;
 501:         while (*tmp != '=')
 502:             fputc(*tmp++,*spoolfile);
 503:         fputc('\n',*spoolfile);
 504:         ++environptr;
 505:     }
 506:     return;
 507: }
 508: 
 509: /*
 510:  * Create the filename for the spoolfile. The format is "yy.ddd.mmmm.??"
 511:  * where "yy" is the year the job will be run, "ddd" the day of year,
 512:  * "mmmm" the hour and minute, and "??" a scratch value used to dis-
 513:  * tinguish between two files that are to be run at the same time.
 514:  */
 515: makeatfile(atfile,year,dayofyear,hour,minute)
 516: int year;
 517: int hour;
 518: int minute;
 519: int dayofyear;
 520: char *atfile;
 521: {
 522:     int i;              /* scratch variable */
 523: 
 524:     for (i=0; ; i += 53) {
 525:         sprintf(atfile, "%s/%02d.%03d.%02d%02d.%02d", ATDIR, year,
 526:             dayofyear, hour, minute, (getpid() + i) % 100);
 527: 
 528:         /*
 529: 		 * Make sure that the file name that we've created is unique.
 530: 		 */
 531:         if (access(atfile, F_OK) == -1)
 532:             return;
 533:     }
 534: }
 535: 
 536: /*
 537:  * Has the requested time already passed for the currrent day? If so, we
 538:  * will run the job "tomorrow".
 539:  */
 540: istomorrow()
 541: {
 542:     if (attime.hour < nowtime.hour)
 543:         return(1);
 544:     if ((attime.hour == nowtime.hour) && (attime.min < nowtime.min))
 545:         return(1);
 546: 
 547:     return(0);
 548: }
 549: 
 550: /*
 551:  * Debugging wreckage.
 552:  */
 553: printit()
 554: {
 555:     printf("YEAR\tnowtime: %d\tattime: %d\n",nowtime.year,attime.year);
 556:     printf("YDAY\tnowtime: %d\tattime: %d\n",nowtime.yday,attime.yday);
 557:     printf("MON\tnowtime: %d\tattime: %d\n",nowtime.mon,attime.mon);
 558:     printf("MONDAY\tnowtime: %d\tattime: %d\n",nowtime.mday,attime.mday);
 559:     printf("WDAY\tnowtime: %d\tattime: %d\n",nowtime.wday,attime.wday);
 560:     printf("HOUR\tnowtime: %d\tattime: %d\n",nowtime.hour,attime.hour);
 561:     printf("MIN\tnowtime: %d\tattime: %d\n",nowtime.min,attime.min);
 562: }
 563: 
 564: /*
 565:  * Calculate the day of year that the job will be executed.
 566:  * The av,ac arguments are ptrs to argv,argc; updated as necessary.
 567:  */
 568: makedayofyear(dateindex, av, ac)
 569: int dateindex;
 570: char ***av;
 571: int *ac;
 572: {
 573:     char **argv = *av;  /* imitate argc,argv and update args at end */
 574:     int argc = *ac;
 575:     char *ptr;              /* scratch pointer */
 576:     struct datetypes *daterequested;    /* pointer to information about
 577: 						   the type of date option
 578: 						   we're dealing with */
 579: 
 580:     daterequested = &dates_info[dateindex];
 581: 
 582:     /*
 583: 	 * If we're dealing with a day of week, determine the number of days
 584: 	 * in the future the next day of this type will fall on. Add this
 585: 	 * value to "attime.yday".
 586: 	 */
 587:     if (daterequested->type == DAY) {
 588:         if (attime.wday < dateindex)
 589:             attime.yday += dateindex - attime.wday;
 590:         else if(attime.wday > dateindex)
 591:             attime.yday += (7 - attime.wday) + dateindex;
 592:         else attime.yday += 7;
 593:     }
 594: 
 595:     /*
 596: 	 * If we're dealing with a month and day of month, determine the
 597: 	 * day of year that this date will fall on.
 598: 	 */
 599:     if (daterequested->type == MONTH) {
 600: 
 601:         /*
 602: 		 * If a day of month isn't specified, print a message
 603: 		 * and exit.
 604: 		 */
 605:         if (argc <= 0) {
 606:             fprintf(stderr,"day of month not specified.\n");
 607:             exit(1);
 608:         }
 609: 
 610:         /*
 611: 		 * Scan the day of month value and make sure that it
 612: 		 * has no characters in it. If characters are found or
 613: 		 * the day requested is zero, print a message and exit.
 614: 		 */
 615:         ptr = *argv;
 616:         while (isdigit(*ptr))
 617:             ++ptr;
 618:         if ((*ptr != '\0') || (atoi(*argv) == 0)) {
 619:             fprintf(stderr,"\"%s\": illegal day of month\n",*argv);
 620:             exit(1);
 621:         }
 622: 
 623:         /*
 624: 		 * Set the month of year and day of month values. Since
 625: 		 * the first 7 values in our dateinfo table do not deal
 626: 		 * with month names, we subtract 7 from the month of year
 627: 		 * value.
 628: 		 */
 629:         attime.mon = (dateindex - 7);
 630:         attime.mday = (atoi(*argv) - 1);
 631: 
 632:         /*
 633: 		 * Test the day of month value to make sure that the
 634: 		 * value is legal.
 635: 		 */
 636:         if ((attime.mday + 1) >
 637:             yeartable[isleap(attime.year)][attime.mon + 1]) {
 638:             fprintf(stderr,"\"%s\": illegal day of month\n",*argv);
 639:             exit(1);
 640:         }
 641: 
 642:         /*
 643: 		 * Finally, we determine the day of year.
 644: 		 */
 645:         attime.yday = (countdays());
 646:         ++argv; --argc;
 647:     }
 648: 
 649:     /*
 650: 	 * If 'week' is specified, add 7 to the day of year.
 651: 	 */
 652:     if ((argc > 0) && (strcmp(*argv,"week") == 0)) {
 653:         attime.yday += 7;
 654:         ++argv; --argc;
 655:     }
 656: 
 657:     /*
 658: 	 * Now that all that is done, see if the requested execution time
 659: 	 * has already passed for this year, and if it has, set execution
 660: 	 * for next year.
 661: 	 */
 662:     if (isnextyear())
 663:         ++attime.year;
 664: 
 665:     /*
 666: 	 * Finally, reflect the updated argc,argv to the caller
 667: 	 */
 668:     *av = argv;
 669:     *ac = argc;
 670: }
 671: 
 672: /*
 673:  * Should the job be run next year? We check for the following situations:
 674:  *
 675:  *	1) the requested time has already passed for the current year.
 676:  *	2) the day of year is greater than the number of days in the year.
 677:  *
 678:  * If either of these tests succeed, we increment "attime.year" by 1.
 679:  * If #2 is true, we also subtract the number of days in the current year
 680:  * from "attime.yday". #2 can only occur if someone specifies a job to
 681:  * be run "tomorrow" on Dec. 31 or if they specify a job to be run a
 682:  * 'week' later and the date is at least Dec. 24. (I think so anyway)
 683:  */
 684: isnextyear()
 685: {   register daysinyear;
 686:     if (attime.yday < nowtime.yday)
 687:         return(1);
 688: 
 689:     if ((attime.yday == nowtime.yday) && (attime.hour < nowtime.hour))
 690:         return(1);
 691: 
 692:     daysinyear = isleap(attime.year) ? 366 : 365;
 693:     if (attime.yday >= daysinyear) {
 694:         attime.yday -= daysinyear;
 695:         return(1);
 696:     }
 697:     if (attime.yday > (isleap(attime.year) ? 366 : 365)) {
 698:         attime.yday -= (isleap(attime.year) ? 366 : 365);
 699:         return(1);
 700:     }
 701: 
 702:     return(0);
 703: }
 704: 
 705: /*
 706:  * Determine the day of year given a month and day of month value.
 707:  */
 708: countdays()
 709: {
 710:     int leap;           /* are we dealing with a leap year? */
 711:     int dayofyear;          /* the day of year after conversion */
 712:     int monthofyear;        /* the month of year that we are
 713: 					   dealing with */
 714: 
 715:     /*
 716: 	 * Are we dealing with a leap year?
 717: 	 */
 718:     leap = isleap(attime.year);
 719: 
 720:     monthofyear = attime.mon;
 721:     dayofyear = attime.mday;
 722: 
 723:     /*
 724: 	 * Determine the day of year.
 725: 	 */
 726:     while (monthofyear > 0)
 727:         dayofyear += yeartable[leap][monthofyear--];
 728: 
 729:     return(dayofyear);
 730: }
 731: 
 732: /*
 733:  * Is a year a leap year?
 734:  */
 735: isleap(year)
 736: int year;
 737: 
 738: {
 739:     return((year%4 == 0 && year%100 != 0) || year%100 == 0);
 740: }
 741: 
 742: getdateindex(date)
 743: char *date;
 744: {
 745:     int i = 0;
 746:     struct datetypes *ptr;
 747: 
 748:     ptr = dates_info;
 749: 
 750:     for (ptr = dates_info; ptr->type != 0; ptr++, i++) {
 751:         if (isprefix(date, ptr->name))
 752:             return(i);
 753:     }
 754:     return(NODATEFOUND);
 755: }
 756: 
 757: isprefix(prefix, fullname)
 758: char *prefix, *fullname;
 759: {
 760:     char ch;
 761:     char *ptr;
 762:     char *ptr1;
 763: 
 764:     ptr = prefix;
 765:     ptr1 = fullname;
 766: 
 767:     while (*ptr) {
 768:         ch = *ptr;
 769:         if (isupper(ch))
 770:             ch = tolower(ch);
 771: 
 772:         if (ch != *ptr1++)
 773:             return(0);
 774: 
 775:         ++ptr;
 776:     }
 777:     return(1);
 778: }
 779: 
 780: getnowtime(nowtime, attime)
 781: struct times *nowtime;
 782: struct times *attime;
 783: {
 784:     struct tm *now;
 785:     struct timeval time;
 786:     struct timezone zone;
 787: 
 788:     if (gettimeofday(&time,&zone) < 0) {
 789:         perror("gettimeofday");
 790:         exit(1);
 791:     }
 792:     now = localtime(&time.tv_sec);
 793: 
 794:     attime->year = nowtime->year = now->tm_year;
 795:     attime->yday = nowtime->yday = now->tm_yday;
 796:     attime->mon = nowtime->mon = now->tm_mon;
 797:     attime->mday = nowtime->mday = now->tm_mday;
 798:     attime->wday = nowtime->wday = now->tm_wday;
 799:     attime->hour = nowtime->hour = now->tm_hour;
 800:     attime->min = nowtime->min = now->tm_min;
 801: }
 802: 
 803: /*
 804:  * This is the same routine used in the old "at", so I won't bother
 805:  * commenting it. It'll give you an idea of what the code looked
 806:  * like when I got it.
 807:  */
 808: maketime(attime,ptr)
 809: char *ptr;
 810: struct times *attime;
 811: {
 812:     int val;
 813:     char *p;
 814: 
 815:     p = ptr;
 816:     val = 0;
 817:     while(isdigit(*p)) {
 818:         val = val*10+(*p++ -'0');
 819:     }
 820:     if (p-ptr < 3)
 821:         val *= HOUR;
 822: 
 823:     for (;;) {
 824:         switch(*p) {
 825: 
 826:         case ':':
 827:             ++p;
 828:             if (isdigit(*p)) {
 829:                 if (isdigit(p[1])) {
 830:                     val +=(10* *p + p[1] - 11*'0');
 831:                     p += 2;
 832:                     continue;
 833:                 }
 834:             }
 835:             fprintf(stderr, "bad time format:\n");
 836:             exit(1);
 837: 
 838:         case 'A':
 839:         case 'a':
 840:             if (val >= HALFDAY+HOUR)
 841:                 val = FULLDAY+1;  /* illegal */
 842:             if (val >= HALFDAY && val <(HALFDAY+HOUR))
 843:                 val -= HALFDAY;
 844:             break;
 845: 
 846:         case 'P':
 847:         case 'p':
 848:             if (val >= HALFDAY+HOUR)
 849:                 val = FULLDAY+1;  /* illegal */
 850:             if (val < HALFDAY)
 851:                 val += HALFDAY;
 852:             break;
 853: 
 854:         case 'n':
 855:         case 'N':
 856:             if ((val == 0) || (val == HALFDAY))
 857:                 val = HALFDAY;
 858:             else
 859:                 val = FULLDAY+1;  /* illegal */
 860:             break;
 861: 
 862:         case 'M':
 863:         case 'm':
 864:             if ((val == 0) || (val == HALFDAY))
 865:                 val = 0;
 866:             else
 867:                 val = FULLDAY+1;  /* illegal */
 868:             break;
 869: 
 870: 
 871:         case '\0':
 872:         case ' ':
 873:             /* 24 hour time */
 874:             if (val == FULLDAY)
 875:                 val -= FULLDAY;
 876:             break;
 877: 
 878:         default:
 879:             fprintf(stderr, "bad time format\n");
 880:             exit(1);
 881: 
 882:         }
 883:         break;
 884:     }
 885:     if (val < 0 || val >= FULLDAY) {
 886:         fprintf(stderr, "time out of range\n");
 887:         exit(1);
 888:     }
 889:     if (val%HOUR >= 60) {
 890:         fprintf(stderr, "illegal minute field\n");
 891:         exit(1);
 892:     }
 893:     attime->hour = val/HOUR;
 894:     attime->min = val%HOUR;
 895: }
 896: 
 897: /*
 898:  * Get the full login name of a person using his/her user id.
 899:  */
 900: char *
 901: getname(uid)
 902: int uid;
 903: {
 904:     struct passwd *pwdinfo;         /* password info structure */
 905: 
 906: 
 907:     if ((pwdinfo = getpwuid(uid)) == 0) {
 908:         perror(uid);
 909:         exit(1);
 910:     }
 911:     return(pwdinfo->pw_name);
 912: }
 913: 
 914: /*
 915:  * Do general cleanup.
 916:  */
 917: cleanup()
 918: {
 919:     if (unlink(atfile) == -1)
 920:         perror(atfile);
 921:     exit(1);
 922: }
 923: 
 924: /*
 925:  * Print usage info and exit.
 926:  */
 927: usage()
 928: {
 929:     fprintf(stderr,"usage: at [-csm] time [date] [filename]\n");
 930:     exit(1);
 931: }

Defined functions

cleanup defined in line 917; used 2 times
copyenvironment defined in line 455; used 1 times
countdays defined in line 708; used 1 times
getdateindex defined in line 742; used 1 times
getname defined in line 900; used 2 times
getnowtime defined in line 780; used 1 times
isleap defined in line 735; used 6 times
isnextyear defined in line 684; used 1 times
isprefix defined in line 757; used 1 times
istomorrow defined in line 540; used 1 times
main defined in line 131; never used
makeatfile defined in line 515; used 1 times
makedayofyear defined in line 568; used 1 times
maketime defined in line 808; used 1 times
printit defined in line 553; used 3 times
usage defined in line 927; used 3 times

Defined variables

atfile defined in line 123; used 9 times
attime defined in line 121; used 63 times
copyright defined in line 8; never used
dates_info defined in line 65; used 3 times
environ defined in line 125; used 2 times
months defined in line 91; never used
nowtime defined in line 121; used 23 times
sccsid defined in line 14; never used
yeartable defined in line 102; used 2 times

Defined struct's

datetypes defined in line 62; used 4 times
times defined in line 113; used 6 times

Defined macros

ATDIR defined in line 51; used 1 times
BOURNE defined in line 46; used 2 times
CSHELL defined in line 47; used 1 times
DAY defined in line 43; used 8 times
FULLDAY defined in line 40; used 7 times
HALFDAY defined in line 39; used 10 times
HOUR defined in line 38; used 9 times
LINSIZ defined in line 53; used 2 times
MONTH defined in line 44; used 13 times
NODATEFOUND defined in line 49; used 4 times
WEEK defined in line 42; never used
Last modified: 1987-02-17
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4942
Valid CSS Valid XHTML 1.0 Strict