|
|
1.1 root 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: }
932:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.