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.10 (Berkeley) 06/27/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. The quotes 385 * also guarantee a quoting of the lines before EOF. 386 */ 387 fprintf(spoolfile, "%s << 'QAZWSXEDCRFVTGBYHNUJMIKOLP'\n", shell); 388 389 /* 390 * Now that we have all the files set up, we can start reading in 391 * the job. 392 */ 393 while (fgets(line, LINSIZ, inputfile) != NULL) 394 fputs(line, spoolfile); 395 396 /* don't put on single quotes, csh doesn't like it */ 397 fprintf(spoolfile, "QAZWSXEDCRFVTGBYHNUJMIKOLP\n"); 398 fclose(inputfile); 399 fclose(spoolfile); 400 401 exit(0); 402 403 } 404 405 /* 406 * Copy the user's environment to the spoolfile in the syntax of the 407 * Bourne shell. After the environment is set up, the proper shell 408 * will be invoked. 409 */ 410 copyenvironment(spoolfile) 411 FILE **spoolfile; 412 { 413 char *tmp; /* scratch pointer */ 414 char **environptr = environ; /* pointer to an environment setting */ 415 416 while(*environptr) { 417 tmp = *environptr; 418 419 /* 420 * We don't want the termcap or terminal entry so skip them. 421 */ 422 if ((strncmp(tmp,"TERM=",5) == 0) || 423 (strncmp(tmp,"TERMCAP=",8) == 0)) { 424 ++environptr; 425 continue; 426 } 427 428 /* 429 * Set up the proper syntax. 430 */ 431 while (*tmp != '=') 432 fputc(*tmp++,*spoolfile); 433 fputc('=', *spoolfile); 434 fputc('\'' , *spoolfile); 435 ++tmp; 436 437 /* 438 * Now copy the entry. 439 */ 440 while (*tmp) { 441 if (*tmp == '\'') 442 fputs("'\\''", *spoolfile); 443 else if (*tmp == '\n') 444 fputs("\\",*spoolfile); 445 else 446 fputc(*tmp, *spoolfile); 447 ++tmp; 448 } 449 fputc('\'' , *spoolfile); 450 451 /* 452 * We need to "export" environment settings. 453 */ 454 fprintf(*spoolfile, "\nexport "); 455 tmp = *environptr; 456 while (*tmp != '=') 457 fputc(*tmp++,*spoolfile); 458 fputc('\n',*spoolfile); 459 ++environptr; 460 } 461 return; 462 } 463 464 /* 465 * Create the filename for the spoolfile. The format is "yy.ddd.mmmm.??" 466 * where "yy" is the year the job will be run, "ddd" the day of year, 467 * "mmmm" the hour and minute, and "??" a scratch value used to dis- 468 * tinguish between two files that are to be run at the same time. 469 */ 470 makeatfile(atfile,year,dayofyear,hour,minute) 471 int year; 472 int hour; 473 int minute; 474 int dayofyear; 475 char *atfile; 476 { 477 int i; /* scratch variable */ 478 479 for (i=0; ; i += 53) { 480 sprintf(atfile, "%s/%02d.%03d.%02d%02d.%02d", _PATH_ATDIR, 481 year, dayofyear, hour, minute, (getpid() + i) % 100); 482 483 /* 484 * Make sure that the file name that we've created is unique. 485 */ 486 if (access(atfile, F_OK) == -1) 487 return; 488 } 489 } 490 491 /* 492 * Has the requested time already passed for the currrent day? If so, we 493 * will run the job "tomorrow". 494 */ 495 istomorrow() 496 { 497 if (attime.hour < nowtime.hour) 498 return(1); 499 if ((attime.hour == nowtime.hour) && (attime.min < nowtime.min)) 500 return(1); 501 502 return(0); 503 } 504 505 /* 506 * Debugging wreckage. 507 */ 508 printit() 509 { 510 printf("YEAR\tnowtime: %d\tattime: %d\n",nowtime.year,attime.year); 511 printf("YDAY\tnowtime: %d\tattime: %d\n",nowtime.yday,attime.yday); 512 printf("MON\tnowtime: %d\tattime: %d\n",nowtime.mon,attime.mon); 513 printf("MONDAY\tnowtime: %d\tattime: %d\n",nowtime.mday,attime.mday); 514 printf("WDAY\tnowtime: %d\tattime: %d\n",nowtime.wday,attime.wday); 515 printf("HOUR\tnowtime: %d\tattime: %d\n",nowtime.hour,attime.hour); 516 printf("MIN\tnowtime: %d\tattime: %d\n",nowtime.min,attime.min); 517 } 518 519 /* 520 * Calculate the day of year that the job will be executed. 521 * The av,ac arguments are ptrs to argv,argc; updated as necessary. 522 */ 523 makedayofyear(dateindex, av, ac) 524 int dateindex; 525 char ***av; 526 int *ac; 527 { 528 char **argv = *av; /* imitate argc,argv and update args at end */ 529 int argc = *ac; 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 (argc <= 0) { 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; --argc; 602 } 603 604 /* 605 * If 'week' is specified, add 7 to the day of year. 606 */ 607 if ((argc > 0) && (strcmp(*argv,"week") == 0)) { 608 attime.yday += 7; 609 ++argv; --argc; 610 } 611 612 /* 613 * Now that all that is done, see if the requested execution time 614 * has already passed for this year, and if it has, set execution 615 * for next year. 616 */ 617 if (isnextyear()) 618 ++attime.year; 619 620 /* 621 * Finally, reflect the updated argc,argv to the caller 622 */ 623 *av = argv; 624 *ac = argc; 625 } 626 627 /* 628 * Should the job be run next year? We check for the following situations: 629 * 630 * 1) the requested time has already passed for the current year. 631 * 2) the day of year is greater than the number of days in the year. 632 * 633 * If either of these tests succeed, we increment "attime.year" by 1. 634 * If #2 is true, we also subtract the number of days in the current year 635 * from "attime.yday". #2 can only occur if someone specifies a job to 636 * be run "tomorrow" on Dec. 31 or if they specify a job to be run a 637 * 'week' later and the date is at least Dec. 24. (I think so anyway) 638 */ 639 isnextyear() 640 { register daysinyear; 641 if (attime.yday < nowtime.yday) 642 return(1); 643 644 if ((attime.yday == nowtime.yday) && (attime.hour < nowtime.hour)) 645 return(1); 646 647 daysinyear = isleap(attime.year) ? 366 : 365; 648 if (attime.yday >= daysinyear) { 649 attime.yday -= daysinyear; 650 return(1); 651 } 652 if (attime.yday > (isleap(attime.year) ? 366 : 365)) { 653 attime.yday -= (isleap(attime.year) ? 366 : 365); 654 return(1); 655 } 656 657 return(0); 658 } 659 660 /* 661 * Determine the day of year given a month and day of month value. 662 */ 663 countdays() 664 { 665 int leap; /* are we dealing with a leap year? */ 666 int dayofyear; /* the day of year after conversion */ 667 int monthofyear; /* the month of year that we are 668 dealing with */ 669 670 /* 671 * Are we dealing with a leap year? 672 */ 673 leap = isleap(attime.year); 674 675 monthofyear = attime.mon; 676 dayofyear = attime.mday; 677 678 /* 679 * Determine the day of year. 680 */ 681 while (monthofyear > 0) 682 dayofyear += yeartable[leap][monthofyear--]; 683 684 return(dayofyear); 685 } 686 687 /* 688 * Is a year a leap year? 689 */ 690 isleap(year) 691 int year; 692 693 { 694 return((year%4 == 0 && year%100 != 0) || year%100 == 0); 695 } 696 697 getdateindex(date) 698 char *date; 699 { 700 int i = 0; 701 struct datetypes *ptr; 702 703 ptr = dates_info; 704 705 for (ptr = dates_info; ptr->type != 0; ptr++, i++) { 706 if (isprefix(date, ptr->name)) 707 return(i); 708 } 709 return(NODATEFOUND); 710 } 711 712 isprefix(prefix, fullname) 713 char *prefix, *fullname; 714 { 715 char ch; 716 char *ptr; 717 char *ptr1; 718 719 ptr = prefix; 720 ptr1 = fullname; 721 722 while (*ptr) { 723 ch = *ptr; 724 if (isupper(ch)) 725 ch = tolower(ch); 726 727 if (ch != *ptr1++) 728 return(0); 729 730 ++ptr; 731 } 732 return(1); 733 } 734 735 getnowtime(nowtime, attime) 736 struct times *nowtime; 737 struct times *attime; 738 { 739 struct tm *now; 740 struct timeval time; 741 struct timezone zone; 742 743 if (gettimeofday(&time,&zone) < 0) { 744 perror("gettimeofday"); 745 exit(1); 746 } 747 now = localtime(&time.tv_sec); 748 749 attime->year = nowtime->year = now->tm_year; 750 attime->yday = nowtime->yday = now->tm_yday; 751 attime->mon = nowtime->mon = now->tm_mon; 752 attime->mday = nowtime->mday = now->tm_mday; 753 attime->wday = nowtime->wday = now->tm_wday; 754 attime->hour = nowtime->hour = now->tm_hour; 755 attime->min = nowtime->min = now->tm_min; 756 } 757 758 /* 759 * This is the same routine used in the old "at", so I won't bother 760 * commenting it. It'll give you an idea of what the code looked 761 * like when I got it. 762 */ 763 maketime(attime,ptr) 764 char *ptr; 765 struct times *attime; 766 { 767 int val; 768 char *p; 769 770 p = ptr; 771 val = 0; 772 while(isdigit(*p)) { 773 val = val*10+(*p++ -'0'); 774 } 775 if (p-ptr < 3) 776 val *= HOUR; 777 778 for (;;) { 779 switch(*p) { 780 781 case ':': 782 ++p; 783 if (isdigit(*p)) { 784 if (isdigit(p[1])) { 785 val +=(10* *p + p[1] - 11*'0'); 786 p += 2; 787 continue; 788 } 789 } 790 fprintf(stderr, "bad time format:\n"); 791 exit(1); 792 793 case 'A': 794 case 'a': 795 if (val >= HALFDAY+HOUR) 796 val = FULLDAY+1; /* illegal */ 797 if (val >= HALFDAY && val <(HALFDAY+HOUR)) 798 val -= HALFDAY; 799 break; 800 801 case 'P': 802 case 'p': 803 if (val >= HALFDAY+HOUR) 804 val = FULLDAY+1; /* illegal */ 805 if (val < HALFDAY) 806 val += HALFDAY; 807 break; 808 809 case 'n': 810 case 'N': 811 if ((val == 0) || (val == HALFDAY)) 812 val = HALFDAY; 813 else 814 val = FULLDAY+1; /* illegal */ 815 break; 816 817 case 'M': 818 case 'm': 819 if ((val == 0) || (val == HALFDAY)) 820 val = 0; 821 else 822 val = FULLDAY+1; /* illegal */ 823 break; 824 825 826 case '\0': 827 case ' ': 828 break; 829 830 default: 831 fprintf(stderr, "bad time format\n"); 832 exit(1); 833 834 } 835 break; 836 } 837 if (val < 0 || val >= FULLDAY) { 838 fprintf(stderr, "time out of range\n"); 839 exit(1); 840 } 841 if (val%HOUR >= 60) { 842 fprintf(stderr, "illegal minute field\n"); 843 exit(1); 844 } 845 attime->hour = val/HOUR; 846 attime->min = val%HOUR; 847 } 848 849 /* 850 * Get the full login name of a person using his/her user id. 851 */ 852 char * 853 getname(uid) 854 int uid; 855 { 856 struct passwd *pwdinfo; /* password info structure */ 857 char *logname, *getlogin(); 858 859 logname = getlogin(); 860 if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL || 861 pwdinfo->pw_uid != uid) 862 pwdinfo = getpwuid(uid); 863 if (pwdinfo == 0) { 864 fprintf(stderr, "no name for uid %d?\n", uid); 865 exit(1); 866 } 867 return(pwdinfo->pw_name); 868 } 869 870 /* 871 * Do general cleanup. 872 */ 873 cleanup() 874 { 875 if (unlink(atfile) == -1) 876 perror(atfile); 877 exit(1); 878 } 879 880 /* 881 * Print usage info and exit. 882 */ 883 usage() 884 { 885 fprintf(stderr,"usage: at [-csm] time [date] [filename]\n"); 886 exit(1); 887 } 888 889