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