# 2.11BSD#431 - /usr/src/usr.bin/at/at.c

##### Functions

    cleanup           X
    copyenvironment   X
    countdays         X
    getdateindex      X
    getname           X
    getnowtime        X
    isleap            X
    isnextyear        X
    isprefix          X
    istomorrow        X
    main              X
    makeatfile        X
    makedayofyear     X
    maketime          X
    printit           X
    usage             X

##### Variables

    atfile            X
    attime            X
    copyright         X
    dates_info        X
    environ           X
    months            X
    nowtime           X

##### Macros

    ATDIR             X
    BOURNE            X
    CSHELL            X
    DAY               X
    FULLDAY           X
    HALFDAY           X
    HOUR              X
    LINSIZ            X
    MONTH             X
    NODATEFOUND       X
    WEEK              X

##### struct's

    datetypes         X
    times             X

       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

- in line 137, 331

copyenvironment defined in line 455; used 1 times

- in line 413

countdays defined in line 708; used 1 times

- in line 645

getdateindex defined in line 742; used 1 times

- in line 226

getname defined in line 900; used 2 times

- in line 151, 386

getnowtime defined in line 780; used 1 times

- in line 193

isleap defined in line 735; used 6 times

- in line 255, 637, 692-698(3), 718

isnextyear defined in line 684; used 1 times

- in line 662

isprefix defined in line 757; used 1 times

- in line 751

istomorrow defined in line 540; used 1 times

- in line 252

main defined in line 131; never used
makeatfile defined in line 515; used 1 times

- in line 286

makedayofyear defined in line 568; used 1 times

- in line 234

maketime defined in line 808; used 1 times

- in line 206

printit defined in line 553; used 3 times

- in line 196, 211, 273

usage defined in line 927; used 3 times

- in line 136, 176, 200

#### Defined variables

atfile defined in line 123; used 9 times

- in line 286-292(3), 515-525(3), 531, 919-920(2)

attime defined in line 121; used 63 times

- in line 193, 206, 250-258(6), 286(4), 542-544(3), 555-561(7), 588-592(7), 629-630(2), 636-637(3), 645, 653, 663, 686-698(10), 718-721(3), 780-782(2), 794-800(7), 808-810(2), 893-894(2)

copyright defined in line 8; never used
dates_info defined in line 65; used 3 times

- in line 580, 748-750(2)

environ defined in line 125; used 2 times

- in line 412, 459

months defined in line 91; never used
nowtime defined in line 121; used 23 times

- in line 193, 542-544(3), 555-561(7), 686-689(3), 780-781(2), 794-800(7)

sccsid defined in line 14; never used
yeartable defined in line 102; used 2 times

- in line 637, 727

#### Defined struct's

datetypes defined in line 62; used 4 times

- in line 576(2), 746(2)

times defined in line 113; used 6 times

- in line 781-782(4), 810(2)

#### Defined macros

ATDIR defined in line 51; used 1 times

- in line 525

BOURNE defined in line 46; used 2 times

- in line 140, 170

CSHELL defined in line 47; used 1 times

- in line 166

DAY defined in line 43; used 8 times

- in line 66-72(7), 587

FULLDAY defined in line 40; used 7 times

- in line 841, 849, 859, 867, 874-875(2), 885

HALFDAY defined in line 39; used 10 times

- in line 840-857(9), 864

HOUR defined in line 38; used 9 times

- in line 39-40(2), 821, 840-842(2), 848, 889-894(3)

LINSIZ defined in line 53; used 2 times

- in line 148, 432

MONTH defined in line 44; used 13 times

- in line 73-84(12), 599

NODATEFOUND defined in line 49; used 4 times

- in line 138, 226, 247, 754

WEEK defined in line 42; never used

Last modified: 1987-02-17 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2325 |