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