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