BSD 4_3_Tahoe release
[unix-history] / usr / src / usr.bin / at / at.c
CommitLineData
d0aeaf5a
DF
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
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
840fc587 13#ifndef lint
ca67e7b4 14static char sccsid[] = "@(#)at.c 5.5 (Berkeley) 1/18/87";
d6fff2c5 15#endif not lint
255d293c 16
30488131 17/*
d6fff2c5
KM
18 * Synopsis: at [-s] [-c] [-m] time [filename]
19 *
255d293c
SW
20 *
21 *
d6fff2c5 22 * Execute commands at a later date.
255d293c
SW
23 *
24 *
d6fff2c5
KM
25 * Modifications by: Steve Wall
26 * Computer Systems Research Group
27 * University of California @ Berkeley
30488131
BJ
28 *
29 */
1ce8fea1
KB
30#include <stdio.h>
31#include <ctype.h>
30488131 32#include <signal.h>
1ce8fea1 33#include <pwd.h>
89ab7eb3 34#include <sys/param.h>
840fc587 35#include <sys/time.h>
255d293c
SW
36#include <sys/file.h>
37
d6fff2c5
KM
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) */
255d293c 41
d6fff2c5
KM
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 */
255d293c 45
e0799b82
KM
46#define BOURNE "/bin/sh" /* run commands with Bourne shell*/
47#define CSHELL "/bin/csh" /* run commands with C shell */
255d293c 48
d6fff2c5 49#define NODATEFOUND -1 /* no date was given on command line */
255d293c 50
d6fff2c5 51#define ATDIR "/usr/spool/at" /* spooling area */
255d293c 52
e0799b82 53#define LINSIZ 256 /* length of input buffer */
30488131 54
255d293c
SW
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 */
62struct datetypes {
d6fff2c5
KM
63 int type;
64 char *name;
255d293c 65} dates_info[22] = {
d6fff2c5
KM
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, ""},
30488131
BJ
86};
87
255d293c
SW
88/*
89 * Months of the year.
90 */
91char *months[13] = {
d6fff2c5
KM
92 "jan", "feb", "mar", "apr", "may", "jun",
93 "jul", "aug", "sep", "oct", "nov", "dec", 0,
30488131
BJ
94};
95
255d293c
SW
96/*
97 * A table of the number of days in each month of the year.
98 *
d6fff2c5
KM
99 * yeartable[0] -- normal year
100 * yeartable[1] -- leap year
255d293c
SW
101 */
102static int yeartable[2][13] = {
d6fff2c5
KM
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},
255d293c
SW
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 */
113struct times {
d6fff2c5
KM
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 */
255d293c
SW
121} attime, nowtime;
122
d6fff2c5
KM
123char atfile[100]; /* name of spoolfile "yy.ddd.hhhh.??" */
124char *getenv(); /* get info on user's environment */
125char **environ; /* user's environment */
126FILE *spoolfile; /* spool file */
127FILE *inputfile; /* input file ("stdin" or "filename") */
89ab7eb3 128char *getwd(); /* used to get current directory info */
255d293c 129
30488131
BJ
130
131main(argc, argv)
255d293c 132int argc;
30488131
BJ
133char **argv;
134{
d6fff2c5
KM
135 int c; /* scratch variable */
136 int usage(); /* print usage info and exit */
137 int cleanup(); /* do cleanup on an interrupt signal */
e0799b82 138 int dateindex = NODATEFOUND; /* if a day is specified, what option
d6fff2c5 139 is it? (mon day, week, dayofweek) */
e0799b82 140 char *shell = BOURNE; /* what shell do we use to run job? */
d6fff2c5
KM
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 */
e0799b82 148 char line[LINSIZ]; /* a line from input file */
89ab7eb3 149 char pwbuf[MAXPATHLEN]; /* the current working directory */
d6fff2c5 150 char *jobfile = "stdin"; /* file containing job to be run */
344505d2 151 char *getname(); /* get the login name of a user */
3ac9f399 152 int pid; /* For forking for security reasons */
d6fff2c5
KM
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);
255d293c
SW
194
195#ifdef DEBUG
d6fff2c5 196 printit();
255d293c
SW
197#endif
198
e0799b82 199 if (argc <= 0)
d6fff2c5 200 usage();
255d293c 201
d6fff2c5
KM
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;
255d293c
SW
208
209#ifdef DEBUG
d6fff2c5
KM
210 printf("\n\nAFTER MAKETIME\n");
211 printit();
255d293c
SW
212#endif
213
d6fff2c5 214 /*
e0799b82 215 * If argv[(2)] exists, this is a request to run a job on a certain
d6fff2c5
KM
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 */
e0799b82 225 if (argc > 0) {
d6fff2c5
KM
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 */
e0799b82 234 makedayofyear(dateindex, &argv, &argc);
d6fff2c5
KM
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) {
08e950fd 248 int daysinyear;
e0799b82 249 if ((argc > 0) && (strcmp(*argv,"week") == 0)) {
d6fff2c5 250 attime.yday += 7;
e0799b82
KM
251 ++argv; --argc;
252 } else if (istomorrow())
d6fff2c5 253 ++attime.yday;
08e950fd
KD
254
255 daysinyear = isleap(attime.year) ? 366 : 365;
256 if (attime.yday >= daysinyear) {
257 attime.yday -= daysinyear;
258 ++attime.year;
259 }
d6fff2c5 260 }
255d293c 261
e0799b82
KM
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
255d293c
SW
270
271#ifdef DEBUG
d6fff2c5
KM
272 printf("\n\nAFTER ADDDAYS\n");
273 printit();
255d293c
SW
274#endif
275
d6fff2c5
KM
276 /*
277 * Start off assuming we're going to read from standard input,
3ac9f399
KM
278 * but if a filename has been given to read from, we will open it
279 * later.
d6fff2c5
KM
280 */
281 inputfile = stdin;
d6fff2c5
KM
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 /*
3ac9f399 297 * Make the file not world readable.
d6fff2c5 298 */
3ac9f399
KM
299 fchmod(fileno(spoolfile), 0400);
300
301 /*
302 * The protection mechanism works like this:
6303198d
MK
303 * We are running ruid=user, euid=spool owner. So far we have been
304 * messing around in the spool directory, so we needed to run
305 * as the owner of the spool directory.
306 * We now need to switch to the user's effective uid
307 * to simplify permission checking. However, we fork first,
308 * so that we can clean up if interrupted.
3ac9f399
KM
309 */
310 signal(SIGINT, SIG_IGN);
311 pid = fork();
312 if (pid == -1) {
313 perror("fork");
314 exit(1);
315 }
316 if (pid) {
317 int wpid, status;
318
319 /*
320 * We are the parent. If the kid has problems,
321 * cleanup the spool directory.
322 */
323 wpid = wait(&status);
324 if (wpid != pid || status) {
325 cleanup();
326 exit(1);
327 }
328 /*
329 * The kid should have alread flushed the buffers.
330 */
331 _exit(0);
332 }
333
334 /*
335 * Exit on interrupt.
336 */
337 signal(SIGINT, SIG_DFL);
338
339 /*
6303198d 340 * We are the kid, give up special permissions.
3ac9f399
KM
341 */
342 setuid(getuid());
343
344 /*
345 * Open the input file with the user's permissions.
346 */
347 if (!standardin) {
348 jobfile = *argv;
349 if ((inputfile = fopen(jobfile, "r")) == NULL) {
350 perror(jobfile);
351 exit(1);
352 }
353 }
e0799b82 354
d6fff2c5
KM
355 /*
356 * Determine what shell we should use to run the job. If the user
357 * didn't explicitly request that his/her current shell be over-
358 * ridden (shflag of cshflag) then we use the current shell.
359 */
e0799b82
KM
360 if ((!shflag) && (!cshflag) && (getenv("SHELL") != NULL))
361 shell = "$SHELL";
d6fff2c5
KM
362
363 /*
364 * Put some standard information at the top of the spoolfile.
365 * This info is used by the other "at"-oriented programs (atq,
366 * atrm, atrun).
367 */
e0799b82
KM
368 fprintf(spoolfile, "# owner: %.127s\n",getname(getuid()));
369 fprintf(spoolfile, "# jobname: %.127s\n",jobfile);
370 fprintf(spoolfile, "# shell: sh\n");
d6fff2c5
KM
371 fprintf(spoolfile, "# notify by mail: %s\n",(mailflag) ? "yes" : "no");
372 fprintf(spoolfile, "\n");
373
374 /*
375 * Set the modes for any files created by the job being run.
376 */
377 c = umask(0);
378 umask(c);
379 fprintf(spoolfile, "umask %.1o\n", c);
380
381 /*
382 * Get the current working directory so we know what directory to
383 * run the job from.
384 */
89ab7eb3
EW
385 if (getwd(pwbuf) == NULL) {
386 fprintf(stderr, "at: can't get working directory\n");
d6fff2c5
KM
387 exit(1);
388 }
89ab7eb3 389 fprintf(spoolfile, "cd %s\n", pwbuf);
d6fff2c5
KM
390
391 /*
392 * Copy the user's environment to the spoolfile.
393 */
394 if (environ) {
e0799b82 395 copyenvironment(&spoolfile);
d6fff2c5
KM
396 }
397
da35d128 398 /*
e0799b82
KM
399 * Put in a line to run the proper shell using the rest of
400 * the file as input. Note that 'exec'ing the shell will
6303198d
MK
401 * cause sh() to leave a /tmp/sh### file around. This line
402 * depends on the shells allowing EOF to end tagged input. The
403 * quotes also guarantee a quoting of the lines before EOF.
da35d128 404 */
6303198d 405 fprintf(spoolfile, "%s << 'QAZWSXEDCRFVTGBYHNUJMIKOLP'\n", shell);
da35d128 406
d6fff2c5
KM
407 /*
408 * Now that we have all the files set up, we can start reading in
6303198d 409 * the job.
d6fff2c5 410 */
6303198d 411 while (fgets(line, LINSIZ, inputfile) != NULL)
d6fff2c5 412 fputs(line, spoolfile);
d6fff2c5
KM
413
414 /*
344505d2 415 * Close all files and change the mode of the spoolfile.
d6fff2c5
KM
416 */
417 fclose(inputfile);
418 fclose(spoolfile);
d6fff2c5
KM
419
420 exit(0);
255d293c
SW
421
422}
423
424/*
e0799b82
KM
425 * Copy the user's environment to the spoolfile in the syntax of the
426 * Bourne shell. After the environment is set up, the proper shell
427 * will be invoked.
255d293c 428 */
e0799b82 429copyenvironment(spoolfile)
255d293c
SW
430FILE **spoolfile;
431{
d6fff2c5
KM
432 char *tmp; /* scratch pointer */
433 char **environptr = environ; /* pointer to an environment setting */
434
435 while(*environptr) {
436 tmp = *environptr;
437
438 /*
439 * We don't want the termcap or terminal entry so skip them.
440 */
e0799b82
KM
441 if ((strncmp(tmp,"TERM=",5) == 0) ||
442 (strncmp(tmp,"TERMCAP=",8) == 0)) {
d6fff2c5
KM
443 ++environptr;
444 continue;
445 }
446
447 /*
e0799b82 448 * Set up the proper syntax.
d6fff2c5 449 */
d6fff2c5
KM
450 while (*tmp != '=')
451 fputc(*tmp++,*spoolfile);
e0799b82
KM
452 fputc('=', *spoolfile);
453 fputc('\'' , *spoolfile);
d6fff2c5
KM
454 ++tmp;
455
456 /*
457 * Now copy the entry.
458 */
459 while (*tmp) {
460 if (*tmp == '\'')
461 fputs("'\\''", *spoolfile);
462 else if (*tmp == '\n')
463 fputs("\\",*spoolfile);
464 else
465 fputc(*tmp, *spoolfile);
466 ++tmp;
467 }
e0799b82 468 fputc('\'' , *spoolfile);
d6fff2c5
KM
469
470 /*
e0799b82 471 * We need to "export" environment settings.
d6fff2c5 472 */
e0799b82
KM
473 fprintf(*spoolfile, "\nexport ");
474 tmp = *environptr;
475 while (*tmp != '=')
476 fputc(*tmp++,*spoolfile);
d6fff2c5
KM
477 fputc('\n',*spoolfile);
478 ++environptr;
479 }
d6fff2c5 480 return;
255d293c
SW
481}
482
483/*
484 * Create the filename for the spoolfile. The format is "yy.ddd.mmmm.??"
485 * where "yy" is the year the job will be run, "ddd" the day of year,
486 * "mmmm" the hour and minute, and "??" a scratch value used to dis-
487 * tinguish between two files that are to be run at the same time.
488 */
489makeatfile(atfile,year,dayofyear,hour,minute)
490int year;
491int hour;
492int minute;
493int dayofyear;
494char *atfile;
495{
d6fff2c5
KM
496 int i; /* scratch variable */
497
498 for (i=0; ; i += 53) {
499 sprintf(atfile, "%s/%02d.%03d.%02d%02d.%02d", ATDIR, year,
500 dayofyear, hour, minute, (getpid() + i) % 100);
501
502 /*
503 * Make sure that the file name that we've created is unique.
504 */
505 if (access(atfile, F_OK) == -1)
506 return;
507 }
30488131
BJ
508}
509
255d293c
SW
510/*
511 * Has the requested time already passed for the currrent day? If so, we
512 * will run the job "tomorrow".
513 */
514istomorrow()
30488131 515{
d6fff2c5
KM
516 if (attime.hour < nowtime.hour)
517 return(1);
518 if ((attime.hour == nowtime.hour) && (attime.min < nowtime.min))
519 return(1);
255d293c 520
d6fff2c5 521 return(0);
30488131
BJ
522}
523
255d293c
SW
524/*
525 * Debugging wreckage.
526 */
527printit()
528{
d6fff2c5
KM
529 printf("YEAR\tnowtime: %d\tattime: %d\n",nowtime.year,attime.year);
530 printf("YDAY\tnowtime: %d\tattime: %d\n",nowtime.yday,attime.yday);
531 printf("MON\tnowtime: %d\tattime: %d\n",nowtime.mon,attime.mon);
532 printf("MONDAY\tnowtime: %d\tattime: %d\n",nowtime.mday,attime.mday);
533 printf("WDAY\tnowtime: %d\tattime: %d\n",nowtime.wday,attime.wday);
534 printf("HOUR\tnowtime: %d\tattime: %d\n",nowtime.hour,attime.hour);
535 printf("MIN\tnowtime: %d\tattime: %d\n",nowtime.min,attime.min);
255d293c 536}
30488131 537
255d293c
SW
538/*
539 * Calculate the day of year that the job will be executed.
e0799b82 540 * The av,ac arguments are ptrs to argv,argc; updated as necessary.
255d293c 541 */
e0799b82 542makedayofyear(dateindex, av, ac)
255d293c 543int dateindex;
e0799b82
KM
544char ***av;
545int *ac;
255d293c 546{
e0799b82
KM
547 char **argv = *av; /* imitate argc,argv and update args at end */
548 int argc = *ac;
d6fff2c5
KM
549 char *ptr; /* scratch pointer */
550 struct datetypes *daterequested; /* pointer to information about
551 the type of date option
552 we're dealing with */
553
554 daterequested = &dates_info[dateindex];
555
556 /*
557 * If we're dealing with a day of week, determine the number of days
558 * in the future the next day of this type will fall on. Add this
559 * value to "attime.yday".
560 */
561 if (daterequested->type == DAY) {
562 if (attime.wday < dateindex)
563 attime.yday += dateindex - attime.wday;
564 else if(attime.wday > dateindex)
565 attime.yday += (7 - attime.wday) + dateindex;
566 else attime.yday += 7;
567 }
568
569 /*
570 * If we're dealing with a month and day of month, determine the
571 * day of year that this date will fall on.
572 */
573 if (daterequested->type == MONTH) {
574
575 /*
576 * If a day of month isn't specified, print a message
577 * and exit.
578 */
e0799b82 579 if (argc <= 0) {
d6fff2c5
KM
580 fprintf(stderr,"day of month not specified.\n");
581 exit(1);
582 }
583
584 /*
585 * Scan the day of month value and make sure that it
586 * has no characters in it. If characters are found or
587 * the day requested is zero, print a message and exit.
588 */
589 ptr = *argv;
590 while (isdigit(*ptr))
591 ++ptr;
592 if ((*ptr != '\0') || (atoi(*argv) == 0)) {
593 fprintf(stderr,"\"%s\": illegal day of month\n",*argv);
594 exit(1);
595 }
596
597 /*
598 * Set the month of year and day of month values. Since
599 * the first 7 values in our dateinfo table do not deal
600 * with month names, we subtract 7 from the month of year
601 * value.
602 */
603 attime.mon = (dateindex - 7);
604 attime.mday = (atoi(*argv) - 1);
605
606 /*
607 * Test the day of month value to make sure that the
608 * value is legal.
609 */
610 if ((attime.mday + 1) >
611 yeartable[isleap(attime.year)][attime.mon + 1]) {
612 fprintf(stderr,"\"%s\": illegal day of month\n",*argv);
613 exit(1);
614 }
615
616 /*
617 * Finally, we determine the day of year.
618 */
619 attime.yday = (countdays());
e0799b82 620 ++argv; --argc;
d6fff2c5
KM
621 }
622
623 /*
624 * If 'week' is specified, add 7 to the day of year.
625 */
e0799b82 626 if ((argc > 0) && (strcmp(*argv,"week") == 0)) {
d6fff2c5 627 attime.yday += 7;
e0799b82
KM
628 ++argv; --argc;
629 }
d6fff2c5
KM
630
631 /*
632 * Now that all that is done, see if the requested execution time
633 * has already passed for this year, and if it has, set execution
634 * for next year.
635 */
636 if (isnextyear())
637 ++attime.year;
e0799b82
KM
638
639 /*
640 * Finally, reflect the updated argc,argv to the caller
641 */
642 *av = argv;
643 *ac = argc;
255d293c
SW
644}
645
646/*
647 * Should the job be run next year? We check for the following situations:
648 *
d6fff2c5
KM
649 * 1) the requested time has already passed for the current year.
650 * 2) the day of year is greater than the number of days in the year.
255d293c
SW
651 *
652 * If either of these tests succeed, we increment "attime.year" by 1.
653 * If #2 is true, we also subtract the number of days in the current year
654 * from "attime.yday". #2 can only occur if someone specifies a job to
655 * be run "tomorrow" on Dec. 31 or if they specify a job to be run a
656 * 'week' later and the date is at least Dec. 24. (I think so anyway)
657 */
658isnextyear()
08e950fd 659{ register daysinyear;
d6fff2c5
KM
660 if (attime.yday < nowtime.yday)
661 return(1);
255d293c 662
d6fff2c5
KM
663 if ((attime.yday == nowtime.yday) && (attime.hour < nowtime.hour))
664 return(1);
255d293c 665
08e950fd
KD
666 daysinyear = isleap(attime.year) ? 366 : 365;
667 if (attime.yday >= daysinyear) {
668 attime.yday -= daysinyear;
d6fff2c5 669 return(1);
08e950fd 670 }
d6fff2c5
KM
671 if (attime.yday > (isleap(attime.year) ? 366 : 365)) {
672 attime.yday -= (isleap(attime.year) ? 366 : 365);
673 return(1);
674 }
255d293c 675
d6fff2c5 676 return(0);
255d293c
SW
677}
678
679/*
680 * Determine the day of year given a month and day of month value.
681 */
682countdays()
683{
d6fff2c5
KM
684 int leap; /* are we dealing with a leap year? */
685 int dayofyear; /* the day of year after conversion */
686 int monthofyear; /* the month of year that we are
687 dealing with */
688
689 /*
690 * Are we dealing with a leap year?
691 */
692 leap = isleap(attime.year);
693
694 monthofyear = attime.mon;
695 dayofyear = attime.mday;
696
697 /*
698 * Determine the day of year.
699 */
700 while (monthofyear > 0)
701 dayofyear += yeartable[leap][monthofyear--];
702
703 return(dayofyear);
255d293c
SW
704}
705
706/*
707 * Is a year a leap year?
708 */
709isleap(year)
710int year;
711
30488131 712{
d6fff2c5 713 return((year%4 == 0 && year%100 != 0) || year%100 == 0);
30488131
BJ
714}
715
255d293c
SW
716getdateindex(date)
717char *date;
30488131 718{
d6fff2c5
KM
719 int i = 0;
720 struct datetypes *ptr;
255d293c 721
d6fff2c5 722 ptr = dates_info;
255d293c 723
d6fff2c5
KM
724 for (ptr = dates_info; ptr->type != 0; ptr++, i++) {
725 if (isprefix(date, ptr->name))
726 return(i);
727 }
e0799b82 728 return(NODATEFOUND);
30488131
BJ
729}
730
255d293c
SW
731isprefix(prefix, fullname)
732char *prefix, *fullname;
30488131 733{
789608a7 734 char ch;
d6fff2c5
KM
735 char *ptr;
736 char *ptr1;
255d293c 737
d6fff2c5
KM
738 ptr = prefix;
739 ptr1 = fullname;
255d293c 740
d6fff2c5 741 while (*ptr) {
789608a7
SW
742 ch = *ptr;
743 if (isupper(ch))
744 ch = tolower(ch);
255d293c 745
789608a7 746 if (ch != *ptr1++)
d6fff2c5 747 return(0);
789608a7
SW
748
749 ++ptr;
d6fff2c5
KM
750 }
751 return(1);
30488131
BJ
752}
753
255d293c
SW
754getnowtime(nowtime, attime)
755struct times *nowtime;
756struct times *attime;
30488131 757{
d6fff2c5
KM
758 struct tm *now;
759 struct timeval time;
760 struct timezone zone;
761
762 if (gettimeofday(&time,&zone) < 0) {
763 perror("gettimeofday");
764 exit(1);
765 }
766 now = localtime(&time.tv_sec);
767
768 attime->year = nowtime->year = now->tm_year;
769 attime->yday = nowtime->yday = now->tm_yday;
770 attime->mon = nowtime->mon = now->tm_mon;
771 attime->mday = nowtime->mday = now->tm_mday;
772 attime->wday = nowtime->wday = now->tm_wday;
773 attime->hour = nowtime->hour = now->tm_hour;
774 attime->min = nowtime->min = now->tm_min;
30488131 775}
255d293c
SW
776
777/*
778 * This is the same routine used in the old "at", so I won't bother
779 * commenting it. It'll give you an idea of what the code looked
780 * like when I got it.
781 */
782maketime(attime,ptr)
783char *ptr;
784struct times *attime;
785{
d6fff2c5
KM
786 int val;
787 char *p;
788
789 p = ptr;
790 val = 0;
791 while(isdigit(*p)) {
792 val = val*10+(*p++ -'0');
793 }
794 if (p-ptr < 3)
795 val *= HOUR;
796
797 for (;;) {
798 switch(*p) {
799
800 case ':':
801 ++p;
802 if (isdigit(*p)) {
803 if (isdigit(p[1])) {
804 val +=(10* *p + p[1] - 11*'0');
805 p += 2;
806 continue;
807 }
808 }
809 fprintf(stderr, "bad time format:\n");
810 exit(1);
811
812 case 'A':
813 case 'a':
814 if (val >= HALFDAY+HOUR)
815 val = FULLDAY+1; /* illegal */
816 if (val >= HALFDAY && val <(HALFDAY+HOUR))
817 val -= HALFDAY;
818 break;
819
820 case 'P':
821 case 'p':
822 if (val >= HALFDAY+HOUR)
823 val = FULLDAY+1; /* illegal */
824 if (val < HALFDAY)
825 val += HALFDAY;
826 break;
827
828 case 'n':
829 case 'N':
e0799b82
KM
830 if ((val == 0) || (val == HALFDAY))
831 val = HALFDAY;
832 else
833 val = FULLDAY+1; /* illegal */
d6fff2c5
KM
834 break;
835
836 case 'M':
837 case 'm':
e0799b82
KM
838 if ((val == 0) || (val == HALFDAY))
839 val = 0;
840 else
841 val = FULLDAY+1; /* illegal */
d6fff2c5
KM
842 break;
843
844
845 case '\0':
846 case ' ':
847 /* 24 hour time */
848 if (val == FULLDAY)
849 val -= FULLDAY;
850 break;
851
852 default:
853 fprintf(stderr, "bad time format\n");
854 exit(1);
855
856 }
857 break;
858 }
859 if (val < 0 || val >= FULLDAY) {
860 fprintf(stderr, "time out of range\n");
861 exit(1);
862 }
863 if (val%HOUR >= 60) {
864 fprintf(stderr, "illegal minute field\n");
865 exit(1);
866 }
e0799b82
KM
867 attime->hour = val/HOUR;
868 attime->min = val%HOUR;
255d293c
SW
869}
870
344505d2
SW
871/*
872 * Get the full login name of a person using his/her user id.
873 */
874char *
875getname(uid)
876int uid;
877{
878 struct passwd *pwdinfo; /* password info structure */
6303198d
MK
879 char *logname, *getlogin();
880
881 logname = getlogin();
882 if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL ||
883 pwdinfo->pw_uid != uid)
884 pwdinfo = getpwuid(uid);
885 if (pwdinfo == 0) {
886 fprintf(stderr, "no name for uid %d?\n", uid);
344505d2
SW
887 exit(1);
888 }
889 return(pwdinfo->pw_name);
890}
891
892/*
893 * Do general cleanup.
894 */
255d293c
SW
895cleanup()
896{
d6fff2c5
KM
897 if (unlink(atfile) == -1)
898 perror(atfile);
899 exit(1);
255d293c
SW
900}
901
344505d2
SW
902/*
903 * Print usage info and exit.
904 */
255d293c
SW
905usage()
906{
e0799b82 907 fprintf(stderr,"usage: at [-csm] time [date] [filename]\n");
d6fff2c5 908 exit(1);
255d293c
SW
909}
910